自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

InnoDB B-TREE 索引怎么計(jì)算 WHERE 條件范圍內(nèi)有多少條記錄?

數(shù)據(jù)庫 MySQL
一個(gè) WHERE 條件范圍(例如 WHERE a >= 100 AND a <= 200),就是一個(gè)掃描區(qū)間 [100, 200],掃描區(qū)間有起點(diǎn)和終點(diǎn),本文中我們把掃描區(qū)間的起點(diǎn)叫作 左端點(diǎn),終點(diǎn)叫作右端點(diǎn)。

MySQL 為一個(gè)表選擇讀取數(shù)據(jù)的方式,取決于這種方式的執(zhí)行成本。

如果 WHERE 條件能夠命中索引(包含主鍵索引、二級(jí)索引),計(jì)算 WHERE 條件范圍內(nèi)的記錄數(shù)量,是計(jì)算使用索引執(zhí)行查詢的成本的關(guān)鍵指標(biāo)。

本文我們就一起來看看這個(gè)關(guān)鍵指標(biāo)是怎么計(jì)算的?

本文內(nèi)容基于 MySQL 8.0.29 源碼。

正文

1、整體概覽

一個(gè) WHERE 條件范圍(例如 WHERE a >= 100 AND a <= 200),就是一個(gè)掃描區(qū)間 [100,200],掃描區(qū)間有起點(diǎn)和終點(diǎn),本文中我們把掃描區(qū)間的起點(diǎn)叫作 左端點(diǎn),終點(diǎn)叫作右端點(diǎn)。

計(jì)算 WHERE 條件范圍內(nèi)有多少條記錄,就是計(jì)算其對(duì)應(yīng)的掃描區(qū)間有多少條記錄,整體來看,會(huì)經(jīng)過兩大步驟:

第 1 步,定位索引葉結(jié)點(diǎn)中掃描區(qū)間左端點(diǎn)、右端點(diǎn)對(duì)應(yīng)的記錄。

這個(gè)過程是從根結(jié)點(diǎn)開始自上而下進(jìn)行的。

經(jīng)過索引的每一層,會(huì)分別把該層中左端點(diǎn)、右端點(diǎn)記錄所在索引頁的頁號(hào),保存到數(shù)組里,得到從根結(jié)點(diǎn)到葉結(jié)點(diǎn)中的左端點(diǎn)、右端點(diǎn)記錄經(jīng)過的索引頁的路徑。

左端點(diǎn)路徑保存在數(shù)組 path1 中,右端點(diǎn)路徑保存在數(shù)組 path2 中,如下圖所示:

圖片

以一個(gè) 3 層的 B-TREE 索引為例,來說明這個(gè)自上而下的定位過程:

定位索引葉結(jié)點(diǎn)中掃描區(qū)間左端點(diǎn)、右端點(diǎn)記錄,是獨(dú)立進(jìn)行的,但是執(zhí)行流程完全一樣,所以放在一起介紹了。

首先,在根結(jié)點(diǎn)中,左端點(diǎn)、右端點(diǎn)記錄都在根結(jié)點(diǎn)范圍內(nèi),path1[0]、path2[0] 中都會(huì)保存根結(jié)點(diǎn)的頁號(hào)。

左右端點(diǎn)對(duì)應(yīng)的記錄,可能是根結(jié)點(diǎn)中的同一條記錄或不同記錄。

讀取根結(jié)點(diǎn)中左端點(diǎn)、右端點(diǎn)記錄指向的子結(jié)點(diǎn)的頁號(hào)。

然后,進(jìn)入左端點(diǎn)、右端點(diǎn)記錄對(duì)應(yīng)的內(nèi)結(jié)點(diǎn),path1[1] 中保存左端點(diǎn)記錄所在內(nèi)結(jié)點(diǎn)的頁號(hào),path2[1] 中保存右端點(diǎn)記錄所在內(nèi)結(jié)點(diǎn)的頁號(hào)。

左右端點(diǎn)對(duì)應(yīng)的記錄,可能是不同內(nèi)結(jié)點(diǎn)中的記錄,也可能是相同內(nèi)結(jié)點(diǎn)中的同一條記錄或不同記錄。

讀取左端點(diǎn)、右端點(diǎn)在各自內(nèi)結(jié)點(diǎn)中對(duì)應(yīng)的記錄指向的葉結(jié)點(diǎn)的頁號(hào)。

最后,進(jìn)入左端點(diǎn)、右端點(diǎn)記錄對(duì)應(yīng)的葉結(jié)點(diǎn),path1[2] 中保存左端點(diǎn)記錄所在葉結(jié)點(diǎn)的頁號(hào),path2[2] 中保存右端點(diǎn)記錄所在葉結(jié)點(diǎn)的頁號(hào)。

左右端點(diǎn)對(duì)應(yīng)的記錄,可能是不同葉結(jié)點(diǎn)中的記錄,也可能是相同葉結(jié)點(diǎn)中的同一條記錄或不同記錄。

關(guān)于定位掃描區(qū)間左端點(diǎn)、右端點(diǎn)記錄的過程,上一篇文章中有詳細(xì)介紹,感興趣的小伙伴可以點(diǎn)擊這個(gè)鏈接閱讀:InnoDB B-TREE

索引怎么定位一條記錄?

第 2 步,計(jì)算掃描區(qū)間的記錄數(shù)量。

經(jīng)過第 1 步,得到了左右端兩個(gè)路徑數(shù)組,數(shù)組中保存著從根結(jié)點(diǎn)到葉結(jié)點(diǎn),掃描區(qū)間左右端點(diǎn)所經(jīng)過的每一個(gè)索引頁的頁號(hào)。

計(jì)算掃描區(qū)間包含多少條記錄,最終是要計(jì)算葉結(jié)點(diǎn)所在的層級(jí),掃描區(qū)間中包含的記錄數(shù)量。

由于葉結(jié)點(diǎn)所在的層級(jí),掃描區(qū)間中的記錄可能會(huì)分散在很多很多個(gè)連續(xù)的葉結(jié)點(diǎn)中,挨個(gè)讀取掃描區(qū)間中的所有葉結(jié)點(diǎn)的記錄數(shù)量是不現(xiàn)實(shí)的,這就需要借助葉結(jié)點(diǎn)的上層結(jié)點(diǎn)了。

上層結(jié)點(diǎn)中的記錄數(shù)量就是其管轄范圍內(nèi)的葉結(jié)點(diǎn)的數(shù)量。

然而,葉結(jié)點(diǎn)的上層結(jié)點(diǎn)所在的層級(jí),也可能有很多很多的同級(jí)結(jié)點(diǎn),那又需要借助更上一層結(jié)點(diǎn),這樣逐層往上推,最終可能需要讀取根結(jié)點(diǎn)有多少個(gè)子結(jié)點(diǎn)。

正是因?yàn)橛?jì)算掃描區(qū)間包含多少條記錄,可能需要依賴上層結(jié)點(diǎn),在源碼的實(shí)現(xiàn)中,這個(gè)過程也是從根結(jié)點(diǎn)自上而下進(jìn)行的。

自上而下進(jìn)行的過程,就是遍歷左端點(diǎn)、右端點(diǎn)路徑數(shù)組,計(jì)算每一層從左端點(diǎn)記錄到右端點(diǎn)記錄這個(gè)區(qū)間內(nèi)包含的記錄數(shù)量,最終到達(dá)葉結(jié)點(diǎn)所在的層級(jí),得到掃描區(qū)間的記錄數(shù)量。

源碼的實(shí)現(xiàn)自上而下進(jìn)行,文章卻不能這么寫,否則陷入代碼細(xì)節(jié),寫出來也很晦澀難懂。

接下來,我們根據(jù)掃描區(qū)間左端點(diǎn)、右端點(diǎn)記錄在葉結(jié)點(diǎn)中的不同位置,分場景介紹計(jì)算掃描區(qū)間的記錄數(shù)量的過程,請(qǐng)大家保持愉悅的心情看下去,可能會(huì)更容易看懂

^_^

2、 場景分析

我們分場景進(jìn)行介紹是為了更好理解,但是,不同場景下都會(huì)涉及到一些又臭又長并且需要重復(fù)描述的定義。

在更好理解的基礎(chǔ)上,我們也要盡量保持內(nèi)容的簡潔,為此,把一些需要重復(fù)描述的定義在這里列出來,并用短一點(diǎn)的描述來代替,以簡化內(nèi)容。

左索引頁記錄數(shù),左端點(diǎn)記錄所在的索引頁中,從左端點(diǎn)記錄的下一條記錄開始,直到當(dāng)前索引頁中的 supremum

偽記錄的上一條記錄為止,這個(gè)范圍內(nèi)的記錄數(shù)量。

圖片

右索引頁記錄數(shù),右端點(diǎn)記錄所在的索引頁中,從當(dāng)前索引頁中的 infimum偽記錄的下一條記錄開始,直到右端點(diǎn)記錄的上一條記錄為止,這個(gè)范圍內(nèi)的記錄數(shù)量。

圖片

左右端點(diǎn)之間記錄數(shù),除了左端點(diǎn)記錄、右端點(diǎn)記錄之外,掃描區(qū)間中的其它用戶記錄的數(shù)量,也就是說,所有索引頁中的 infimum、supremum

偽記錄都不包含在內(nèi)。

圖片

(1) 同一條記錄

掃描區(qū)間左端點(diǎn)、右端點(diǎn)記錄是葉結(jié)點(diǎn)中的同一條記錄,除去左右端點(diǎn)記錄之外,區(qū)間之內(nèi)沒有其它記錄,左右端點(diǎn)之間記錄數(shù) = 0。

但是,這并不意味著掃描區(qū)間的記錄數(shù)量就為 0。

因?yàn)椋瑨呙鑵^(qū)間的記錄數(shù)量 = 左右端點(diǎn)之間的記錄數(shù) + 左端點(diǎn)記錄 + 右端點(diǎn)記錄。

處理左右端點(diǎn)記錄的計(jì)數(shù)邏輯,然后得到掃描區(qū)間記錄數(shù)量,這個(gè)計(jì)算過程是所有場景共用的,會(huì)在第 2 節(jié)的結(jié)尾處單獨(dú)用一個(gè)小節(jié)來介紹,這里先按下不表。

(2)同一個(gè)葉結(jié)點(diǎn)中的不同記錄

掃描區(qū)間左端點(diǎn)、右端點(diǎn)記錄是同一個(gè)葉結(jié)點(diǎn)中的不同記錄,計(jì)算邏輯也比較簡單。

左右端點(diǎn)之間記錄數(shù) = 右端點(diǎn)記錄之前的記錄數(shù)量 - 左端點(diǎn)記錄之前的記錄數(shù)量。

左右端點(diǎn)記錄之前的記錄數(shù)量,指的是它們共同所在的索引頁中,左右端點(diǎn)記錄各自前面有多少條記錄,如下圖所示:

圖片

(3)相鄰葉結(jié)點(diǎn)中的記錄

掃描區(qū)間左端點(diǎn)、右端點(diǎn)記錄是相鄰葉結(jié)點(diǎn)中的記錄,計(jì)算邏輯依然比較簡單。

左右端點(diǎn)之間記錄數(shù) = 左索引頁記錄數(shù) + 右索引頁記錄數(shù)。

圖片

(4)相隔小于等于 9 個(gè)葉結(jié)點(diǎn)

掃描區(qū)間左端點(diǎn)、右端點(diǎn)記錄所在的索引頁,中間隔著其它索引頁,計(jì)算邏輯開始變得有一點(diǎn)點(diǎn)復(fù)雜了。

左右端點(diǎn)之間記錄數(shù) = 左索引頁記錄數(shù) + 中間索引頁用戶記錄數(shù)之和 + 右索引頁記錄數(shù)。

圖片

上面算式中,中間索引頁用戶記錄數(shù)之和計(jì)算邏輯如下:

從掃描區(qū)間左端點(diǎn)記錄所在索引頁的下一個(gè)索引頁開始,從每個(gè)索引頁的頭信息 PAGE_N_RECS中讀取索引頁中的用戶記錄數(shù)量并累加求和,直到掃描區(qū)間右端點(diǎn)記錄所在索引頁的上一個(gè)索引頁為止。

PAGE_N_RECS 中不包含 infimum、supremum 偽記錄。

圖片

(5)相隔大于 9 個(gè)葉結(jié)點(diǎn)

前面幾個(gè)小節(jié)介紹的場景,計(jì)算掃描區(qū)間記錄數(shù)量的邏輯,都是精確計(jì)算。

本小節(jié)介紹的場景是:掃描區(qū)間左端點(diǎn)、右端點(diǎn)記錄所在的索引頁,中間隔著大于 9 個(gè)索引頁,如下圖所示:

圖片

此場景下,InnoDB不會(huì)讀取掃描區(qū)間內(nèi)所有索引頁中的用戶記錄數(shù)量,而是只讀取前面一部分索引頁中的用戶記錄數(shù)量,并據(jù)此估算出掃描區(qū)間內(nèi)的用戶記錄數(shù)量,估算過程是這樣的:

第 1 步,從掃描區(qū)間左端點(diǎn)記錄所在索引頁的下一個(gè)索引頁開始,連續(xù)讀取 9 個(gè)索引頁中的用戶記錄數(shù)量并累加求和。

每個(gè)索引頁中的用戶記錄數(shù)量都是從索引頁的頭信息 PAGE_N_RECS 中讀取,不包含 infimum、supremum 偽記錄。

第 2 步,第 1 步得到的 9 個(gè)索引頁的用戶記錄數(shù)量之和 + 左索引頁記錄數(shù) + 右索引頁記錄數(shù),得到計(jì)算結(jié)果,InnoDB 把這個(gè)結(jié)果當(dāng)作 10

個(gè)索引頁的用戶記錄數(shù)量之和。

圖片

第 3 步,第 2 步得到的10 個(gè)索引頁的用戶記錄數(shù)量之和,除以 10,得到的計(jì)算結(jié)果,作為掃描區(qū)間范圍內(nèi)每個(gè)索引頁的平均用戶記錄數(shù)。

第 4 步,第 3 步得到的平均用戶記錄數(shù) * 左右端點(diǎn)之間間隔的索引頁數(shù)(如下圖所示),得到間隔索引頁的用戶記錄數(shù)量之和。

圖片

左右端點(diǎn)記錄之間的索引頁數(shù),就是對(duì)上一層級(jí)進(jìn)行 2.1 ~ 2.5 小節(jié)的計(jì)算邏輯得到。

第 5 步,左右端點(diǎn)之間記錄數(shù) = 第 4 步得到的左右端點(diǎn)記錄之間間隔索引頁的用戶記錄數(shù)量之和 + 左索引頁記錄數(shù) + 右索引頁記錄數(shù)。

圖片

前面說了在估算場景下,InnoDB 會(huì)用 10 個(gè)索引頁中的用戶記錄數(shù)量之和計(jì)算每個(gè)索引的平均用戶記錄數(shù)。

為什么本小節(jié)的標(biāo)題是左右端點(diǎn)之間相隔大于 9 個(gè)索引頁?

因?yàn)?InnoDB 把左索引頁記錄數(shù)、右索引頁記錄數(shù)加起來當(dāng)作一個(gè)索引頁的用戶記錄數(shù)量,再加上從掃描區(qū)間左端點(diǎn)記錄所在索引頁的下一個(gè)索引頁開始讀取的 9

個(gè)索引頁中的用戶記錄數(shù)量之和,就是 10 個(gè)索引頁的用戶記錄數(shù)量了。

(6) 處理左右端點(diǎn)記錄的計(jì)數(shù)邏輯

前面提到過,掃描區(qū)間的記錄數(shù)量 = 左右端點(diǎn)之間記錄數(shù) + 左端點(diǎn)記錄(0 或 1) + 右端點(diǎn)記錄(0 或 1)。

這是所有場景共用的邏輯,在這里單獨(dú)用一小節(jié)來介紹。

如果掃描區(qū)間左端點(diǎn)是閉區(qū)間(例如 WHERE a >= 100),則左端點(diǎn)記錄需要計(jì)入掃描區(qū)間的記錄數(shù)量,上面算式中,左端點(diǎn)記錄括號(hào)內(nèi)取

0。

否則不計(jì)入,上面算式中,左端點(diǎn)記錄括號(hào)內(nèi)取 1。

如果掃描區(qū)間右端點(diǎn)是閉區(qū)間(例如 WHERE a <= 200),則右端點(diǎn)記錄需要計(jì)入掃描區(qū)間的記錄數(shù)量,上面算式中,右端點(diǎn)記錄括號(hào)內(nèi)取

0。

否則不計(jì)入,上面算式中,右端點(diǎn)記錄括號(hào)內(nèi)取 1。

然后,就得到了掃描區(qū)間的記錄數(shù)量。

不過,別著急,這有可能還不是最終結(jié)果。

(7) 修正掃描區(qū)間記錄數(shù)量

經(jīng)過 2.6 小節(jié)中左右端點(diǎn)記錄的計(jì)數(shù)邏輯處理,已經(jīng)得到了掃描區(qū)間的記錄數(shù)量。

如果掃描區(qū)間左端點(diǎn)、右端點(diǎn)記錄所在的索引頁,中間隔著大于 9

個(gè)索引頁(也就是估算場景),計(jì)算得到掃描區(qū)間的記錄數(shù)量之后,還需要對(duì)這個(gè)數(shù)量進(jìn)行一系列修正。

首先,InnoDB 認(rèn)為估算的記錄數(shù)量都會(huì)比實(shí)際數(shù)量少,所以,會(huì)對(duì)前面計(jì)算得到的掃描區(qū)間記錄數(shù)量乘以 2,得到掃描區(qū)間的修正記錄數(shù)量,即修正記錄數(shù)量 =

掃描區(qū)間的記錄數(shù)量 * 2。

然后,InnoDB

不會(huì)讓估算記錄數(shù)量大于表中記錄數(shù)量的一半,如果掃描區(qū)間的修正記錄數(shù)量超過表中記錄數(shù)量的一半,就把修正記錄數(shù)量設(shè)置為表中記錄數(shù)量的一半。

最后,修改記錄數(shù)量就是掃描區(qū)間的記錄數(shù)量,這才是最終結(jié)果。

(8)小結(jié)

前面分場景介紹計(jì)算掃描區(qū)間的記錄數(shù)量的過程,為了保持文章盡量簡潔,把處理左右端點(diǎn)記錄的計(jì)數(shù)邏輯(2.6 小節(jié))、修正掃描區(qū)間記錄數(shù)量(2.7小節(jié))獨(dú)立成為兩個(gè)小節(jié),有一點(diǎn)零散。

本小節(jié)以一張流程圖來對(duì)前面的計(jì)算過程進(jìn)行總結(jié),如下:

圖片

三、 總結(jié)

第 2 節(jié) 以定位索引葉結(jié)點(diǎn)中掃描區(qū)間左端點(diǎn)、右端點(diǎn)對(duì)應(yīng)的記錄開始,介紹了計(jì)算掃描區(qū)間記錄數(shù)量的整體過程。

第 3 節(jié) 根據(jù)索引葉結(jié)點(diǎn)中,左右端點(diǎn)記錄所在位置的不同,分 5 種場景介紹了計(jì)算掃描區(qū)間記錄數(shù)量的詳細(xì)過程。

經(jīng)過 5 種場景計(jì)算得到左右端點(diǎn)之間記錄數(shù)之后,再進(jìn)行左右端點(diǎn)記錄的計(jì)數(shù)邏輯處理,得到掃描區(qū)間的記錄數(shù)量。

對(duì)于精確計(jì)算場景,這就是最終的結(jié)果了。

對(duì)于估算場景,還需要對(duì)掃描區(qū)間的記錄數(shù)量進(jìn)行一系列修正,才能得到估算場景下的最終結(jié)果。

本文轉(zhuǎn)載自微信公眾號(hào)「一樹一溪」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系一樹一溪公眾號(hào)。

責(zé)任編輯:姜華 來源: 一樹一溪
相關(guān)推薦

2022-05-25 08:01:37

WHERESQL 語句

2010-10-12 16:50:14

MySQL Hash索

2023-01-26 00:59:39

B-Treegolang度量衡

2024-02-27 07:35:55

B-TreeB+TreeMySQL

2023-05-26 14:08:00

Where 條件MySQL

2023-01-30 23:04:10

B-Treegolang優(yōu)化

2022-10-29 08:44:39

分布式數(shù)據(jù)庫存儲(chǔ)

2022-03-07 14:57:36

MySQLInnoDB索引

2024-05-22 09:01:53

InnoDBB+索引

2024-09-04 08:44:18

MySQL核心模塊

2024-10-16 11:11:51

隔離InnoDB死鎖

2021-04-12 10:52:10

InnoDB索引數(shù)據(jù)庫

2018-08-26 06:24:58

MySQLInnoDB行鎖

2024-05-27 00:02:00

UpdateWhere索引

2013-03-05 09:16:33

MySQLInnodb

2021-08-26 07:43:45

B+ 樹索引磁盤

2013-01-05 09:45:00

微軟Windows AzuCDN服務(wù)

2013-04-25 10:50:37

iCloud宕機(jī)

2010-03-04 09:45:45

IBM裁員

2015-11-03 11:15:54

負(fù)載均衡微服務(wù)web
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)