2019-10-14Mysql索引的詳解知識送給大家

背景

使用mysql最多的就是查詢昔榴,我們迫切的希望mysql能查詢的更快一些蹦浦,我們經(jīng)常用到的查詢有:

按照id查詢唯一一條記錄

按照某些個字段查詢對應(yīng)的記錄

查找某個范圍的所有記錄(between and)

對查詢出來的結(jié)果排序

mysql的索引的目的是使上面的各種查詢能夠更快媳搪。

預(yù)備知識

什么是索引?

上一篇中有詳細(xì)的介紹露懒,可以過去看一下:什么是索引寡具?

索引的本質(zhì):通過不斷地縮小想要獲取數(shù)據(jù)的范圍來篩選出最終想要的結(jié)果,同時把隨機的事件變成順序的事件铐维,也就是說柬泽,有了這種索引機制,我們可以總是用同一種查找方式來鎖定數(shù)據(jù)嫁蛇。

磁盤中數(shù)據(jù)的存取

以機械硬盤來說锨并,先了解幾個概念。

扇區(qū):磁盤存儲的最小單位睬棚,扇區(qū)一般大小為512Byte第煮。

磁盤塊:文件系統(tǒng)與磁盤交互的的最小單位(計算機系統(tǒng)讀寫磁盤的最小單位),一個磁盤塊由連續(xù)幾個(2^n)扇區(qū)組成抑党,塊一般大小一般為4KB包警。

磁盤讀取數(shù)據(jù):磁盤讀取數(shù)據(jù)靠的是機械運動,每次讀取數(shù)據(jù)花費的時間可以分為尋道時間底靠、旋轉(zhuǎn)延遲害晦、傳輸時間三個部分,尋道時間指的是磁臂移動到指定磁道所需要的時間苛骨,主流磁盤一般在5ms以下篱瞎;旋轉(zhuǎn)延遲就是我們經(jīng)常聽說的磁盤轉(zhuǎn)速,比如一個磁盤7200轉(zhuǎn)痒芝,表示每分鐘能轉(zhuǎn)7200次俐筋,也就是說1秒鐘能轉(zhuǎn)120次,旋轉(zhuǎn)延遲就是1/120/2 = 4.17ms严衬;傳輸時間指的是從磁盤讀出或?qū)?shù)據(jù)寫入磁盤的時間澄者,一般在零點幾毫秒,相對于前兩個時間可以忽略不計。那么訪問一次磁盤的時間粱挡,即一次磁盤IO的時間約等于5+4.17 = 9ms左右赠幕,聽起來還挺不錯的,但要知道一臺500 -MIPS的機器每秒可以執(zhí)行5億條指令询筏,因為指令依靠的是電的性質(zhì)榕堰,換句話說執(zhí)行一次IO的時間可以執(zhí)行40萬條指令,數(shù)據(jù)庫動輒十萬百萬乃至千萬級數(shù)據(jù)嫌套,每次9毫秒的時間逆屡,顯然是個災(zāi)難。

mysql中的頁

mysql中和磁盤交互的最小單位稱為頁踱讨,頁是mysql內(nèi)部定義的一種數(shù)據(jù)結(jié)構(gòu)魏蔗,默認(rèn)為16kb,相當(dāng)于4個磁盤塊痹筛,也就是說mysql每次從磁盤中讀取一次數(shù)據(jù)是16KB莺治,要么不讀取,要讀取就是16KB帚稠,此值可以修改的谣旁。

數(shù)據(jù)檢索過程

我們對數(shù)據(jù)存儲方式不做任何優(yōu)化,直接將數(shù)據(jù)庫中表的記錄存儲在磁盤中滋早,假如某個表只有一個字段蔓挖,為int類型,int占用4個byte馆衔,每個磁盤塊可以存儲1000條記錄瘟判,100萬的記錄需要1000個磁盤塊,如果我們需要從這100萬記錄中檢索所需要的記錄角溃,需要讀取1000個磁盤塊的數(shù)據(jù)(需要1000次io)拷获,每次io需要9ms,那么1000次需要9000ms=9s减细,100條數(shù)據(jù)隨便一個查詢就是9秒匆瓜,這種情況我們是無法接受的,顯然是不行的未蝌。

我們迫切的需求是什么驮吱?

我們迫切需要這樣的數(shù)據(jù)結(jié)構(gòu)和算法:

需要一種數(shù)據(jù)存儲結(jié)構(gòu):當(dāng)從磁盤中檢索數(shù)據(jù)的時候能,夠減少磁盤的io次數(shù)萧吠,最好能夠降低到一個穩(wěn)定的常量值

需要一種檢索算法:當(dāng)從磁盤中讀取磁盤塊的數(shù)據(jù)之后左冬,這些塊中可能包含多條記錄,這些記錄被加載到內(nèi)存中纸型,那么需要一種算法能夠快速從內(nèi)存多條記錄中快速檢索出目標(biāo)數(shù)據(jù)

我們來找找拇砰,看是否能夠找到這樣的算法和數(shù)據(jù)結(jié)構(gòu)梅忌。

我們看一下常見的檢索算法和數(shù)據(jù)結(jié)構(gòu)。

循環(huán)遍歷查找

從一組無序的數(shù)據(jù)中查找目標(biāo)數(shù)據(jù)除破,常見的方法是遍歷查詢牧氮,n條數(shù)據(jù),時間復(fù)雜度為O(n)瑰枫,最快需要1次踱葛,最壞的情況需要n次,查詢效率不穩(wěn)定光坝。

二分法查找

二分法查找也稱為折半查找剖毯,用于在一個有序數(shù)組中快速定義某一個需要查找的數(shù)據(jù)。

原理是:

先將一組無序的數(shù)據(jù)排序(升序或者降序)之后放在數(shù)組中教馆,此處用升序來舉例說明:用數(shù)組中間位置的數(shù)據(jù)A和需要查找的數(shù)據(jù)F對比,如果A=F擂达,則結(jié)束查找土铺;如果A<F,則將查找的范圍縮小至數(shù)組中A數(shù)據(jù)右邊的部分板鬓;如果A>F悲敷,則將查找范圍縮小至數(shù)組中A數(shù)據(jù)左邊的部分,繼續(xù)按照上面的方法直到找到F為止俭令。

示例:

從下列有序數(shù)字中查找數(shù)字9后德,過程如下

[1,2,3,4,5,6,7,8,9]

第1次查找:[1,2,3,4,5,6,7,8,9]中間位置值為5,9>5抄腔,將查找范圍縮小至5右邊的部分:[6瓢湃、7、8赫蛇、9]

第2次查找:[6绵患、7、8悟耘、9]中間值為8落蝙,9>8 ,將范圍縮小至8右邊部分:[9]

第3次查找:在[9]中查找9暂幼,找到了筏勒。

可以看到查找速度是相當(dāng)快的,每次查找都會使范圍減半旺嬉,如果我們采用順序查找管行,上面數(shù)據(jù)最快需要1次,最多需要9次邪媳,而二分法查找最多只需要3次病瞳,耗時時間也比較穩(wěn)定揽咕。

二分法查找時間復(fù)雜度是:O(logN)(N為數(shù)據(jù)量),100萬數(shù)據(jù)查找最多只需要20次(2^20=1048576)

二分法查找數(shù)據(jù)的優(yōu)點:定位數(shù)據(jù)非程撞耍快亲善,前提是:目標(biāo)數(shù)組是有序的。

有序數(shù)組

如果我們將mysql中表的數(shù)據(jù)以有序數(shù)組的方式存儲在磁盤中逗柴,那么我們定位數(shù)據(jù)步驟是:

取出目標(biāo)表的所有數(shù)據(jù)蛹头,存放在一個有序數(shù)組中

如果目標(biāo)表的數(shù)據(jù)量非常大,從磁盤中加載到內(nèi)存中需要的內(nèi)存也非常大

步驟取出所有數(shù)據(jù)耗費的io次數(shù)太多戏溺,步驟2耗費的內(nèi)存空間太大渣蜗,還有新增數(shù)據(jù)的時候,為了保證數(shù)組有序旷祸,插入數(shù)據(jù)會涉及到數(shù)組內(nèi)部數(shù)據(jù)的移動耕拷,也是比較耗時的,顯然用這種方式存儲數(shù)據(jù)是不可取的托享。

鏈表

鏈表相當(dāng)于在每個節(jié)點上增加一些指針骚烧,可以和前面或者后面的節(jié)點連接起來,就像一列火車一樣闰围,每節(jié)車廂相當(dāng)于一個節(jié)點赃绊,車廂內(nèi)部可以存儲數(shù)據(jù),每個車廂和下一節(jié)車廂相連羡榴。

鏈表分為單鏈表和雙向鏈表碧查。

單鏈表

每個節(jié)點中有持有指向下一個節(jié)點的指針,只能按照一個方向遍歷鏈表校仑,結(jié)構(gòu)如下:

//單項鏈表

class Node1{

private Object data;//存儲數(shù)據(jù)

private Node1 nextNode;//指向下一個節(jié)點

}

雙向鏈表

每個節(jié)點中兩個指針忠售,分別指向當(dāng)前節(jié)點的上一個節(jié)點和下一個節(jié)點,結(jié)構(gòu)如下:

//雙向鏈表

class Node2{

private Object data;//存儲數(shù)據(jù)

private Node1 prevNode;//指向上一個節(jié)點

private Node1 nextNode;//指向下一個節(jié)點

}

鏈表的優(yōu)點:

可以快速定位到上一個或者下一個節(jié)點

可以快速刪除數(shù)據(jù)迄沫,只需改變指針的指向即可档痪,這點比數(shù)組好

鏈表的缺點:

無法向數(shù)組那樣,通過下標(biāo)隨機訪問數(shù)據(jù)

查找數(shù)據(jù)需從第一個節(jié)點開始遍歷邢滑,不利于數(shù)據(jù)的查找腐螟,查找時間和無需數(shù)據(jù)類似,需要全遍歷困后,最差時間是O(N)

二叉查找樹

二叉樹是每個結(jié)點最多有兩個子樹的樹結(jié)構(gòu)乐纸,通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。二叉樹常被用于實現(xiàn)二叉查找樹和二叉堆摇予。二叉樹有如下特性:

1汽绢、每個結(jié)點都包含一個元素以及n個子樹,這里0≤n≤2侧戴。2宁昭、左子樹和右子樹是有順序的跌宛,次序不能任意顛倒,左子樹的值要小于父結(jié)點积仗,右子樹的值要大于父結(jié)點疆拘。

數(shù)組[20,10,5,15,30,25,35]使用二叉查找樹存儲如下:

Mysql索引的詳解知識送給大家

每個節(jié)點上面有兩個指針(left,rigth),可以通過這2個指針快速訪問左右子節(jié)點寂曹,檢索任何一個數(shù)據(jù)最多只需要訪問3個節(jié)點哎迄,相當(dāng)于訪問了3次數(shù)據(jù),時間為O(logN)隆圆,和二分法查找效率一樣漱挚,查詢數(shù)據(jù)還是比較快的。

但是如果我們插入數(shù)據(jù)是有序的渺氧,如[5,10,15,20,30,25,35]旨涝,那么結(jié)構(gòu)就變成下面這樣:

Mysql索引的詳解知識送給大家

二叉樹退化為了一個鏈表結(jié)構(gòu),查詢數(shù)據(jù)最差就變?yōu)榱薕(N)侣背。

二叉樹的優(yōu)缺點:

查詢數(shù)據(jù)的效率不穩(wěn)定白华,若樹左右比較平衡的時,最差情況為O(logN)秃踩,如果插入數(shù)據(jù)是有序的,退化為了鏈表业筏,查詢時間變成了O(N)

數(shù)據(jù)量大的情況下憔杨,會導(dǎo)致樹的高度變高,如果每個節(jié)點對應(yīng)磁盤的一個塊來存儲一條數(shù)據(jù)蒜胖,需io次數(shù)大幅增加消别,顯然用此結(jié)構(gòu)來存儲數(shù)據(jù)是不可取的

平衡二叉樹(AVL樹)

平衡二叉樹是一種特殊的二叉樹,所以他也滿足前面說到的二叉查找樹的兩個特性台谢,同時還有一個特性:

它的左右兩個子樹的高度差的絕對值不超過1寻狂,并且左右兩個子樹都是一棵平衡二叉樹。

平衡二叉樹相對于二叉樹來說朋沮,樹的左右比較平衡蛇券,不會出現(xiàn)二叉樹那樣退化成鏈表的情況,不管怎么插入數(shù)據(jù)樊拓,最終通過一些調(diào)整纠亚,都能夠保證樹左右高度相差不大于1。

這樣可以讓查詢速度比較穩(wěn)定筋夏,查詢中遍歷節(jié)點控制在O(logN)范圍內(nèi)

如果數(shù)據(jù)都存儲在內(nèi)存中蒂胞,采用AVL樹來存儲,還是可以的条篷,查詢效率非常高骗随。不過我們的數(shù)據(jù)是存在磁盤中蛤织,用過采用這種結(jié)構(gòu),每個節(jié)點對應(yīng)一個磁盤塊鸿染,數(shù)據(jù)量大的時候指蚜,也會和二叉樹一樣,會導(dǎo)致樹的高度變高牡昆,增加了io次數(shù)姚炕,顯然用這種結(jié)構(gòu)存儲數(shù)據(jù)也是不可取的。

B-樹

B杠樹丢烘,千萬不要讀作B減樹了柱宦,B-樹在是平衡二叉樹上進(jìn)化來的,前面介紹的幾種樹播瞳,每個節(jié)點上面只有一個元素掸刊,而B-樹節(jié)點中可以放多個元素,主要是為了降低樹的高度赢乓。

一棵m階的B-Tree有如下特性【特征描述的有點繞忧侧,看不懂的可以跳過,看后面的圖】:

每個節(jié)點最多有m個孩子牌芋,m稱為b樹的階

除了根節(jié)點和葉子節(jié)點外蚓炬,其它每個節(jié)點至少有Ceil(m/2)個孩子

若根節(jié)點不是葉子節(jié)點,則至少有2個孩子

所有葉子節(jié)點都在同一層躺屁,且不包含其它關(guān)鍵字信息

每個非終端節(jié)點包含n個關(guān)鍵字(健值)信息

關(guān)鍵字的個數(shù)n滿足:ceil(m/2)-1 <= n <= m-1

ki(i=1,…n)為關(guān)鍵字肯夏,且關(guān)鍵字升序排序

Pi(i=1,…n)為指向子樹根節(jié)點的指針。P(i-1)指向的子樹的所有節(jié)點關(guān)鍵字均小于ki犀暑,但都大于k(i-1)

B-Tree結(jié)構(gòu)的數(shù)據(jù)可以讓系統(tǒng)高效的找到數(shù)據(jù)所在的磁盤塊驯击。為了描述B-Tree,首先定義一條記錄為一個二元組[key, data] 耐亏,key為記錄的鍵值徊都,對應(yīng)表中的主鍵值,data為一行記錄中除主鍵外的數(shù)據(jù)广辰。對于不同的記錄暇矫,key值互不相同。

B-Tree中的每個節(jié)點根據(jù)實際情況可以包含大量的關(guān)鍵字信息和分支择吊,如下圖所示為一個3階的B-Tree:

Mysql索引的詳解知識送給大家

每個節(jié)點占用一個盤塊的磁盤空間袱耽,一個節(jié)點上有兩個升序排序的關(guān)鍵字和三個指向子樹根節(jié)點的指針,指針存儲的是子節(jié)點所在磁盤塊的地址干发。兩個鍵將數(shù)據(jù)劃分成的三個范圍域朱巨,對應(yīng)三個指針指向的子樹的數(shù)據(jù)的范圍域。以根節(jié)點為例枉长,關(guān)鍵字為17和35冀续,P1指針指向的子樹的數(shù)據(jù)范圍為小于17琼讽,P2指針指向的子樹的數(shù)據(jù)范圍為17~35,P3指針指向的子樹的數(shù)據(jù)范圍為大于35洪唐。

模擬查找關(guān)鍵字29的過程:

根據(jù)根節(jié)點找到磁盤塊1钻蹬,讀入內(nèi)存∑拘瑁【磁盤I/O操作第1次】

比較關(guān)鍵字29在區(qū)間(17,35)问欠,找到磁盤塊1的指針P2

根據(jù)P2指針找到磁盤塊3,讀入內(nèi)存粒蜈∷诚祝【磁盤I/O操作第2次】

比較關(guān)鍵字29在區(qū)間(26,30),找到磁盤塊3的指針P2

根據(jù)P2指針找到磁盤塊8枯怖,讀入內(nèi)存注整。【磁盤I/O操作第3次】

在磁盤塊8中的關(guān)鍵字列表中找到關(guān)鍵字29

分析上面過程度硝,發(fā)現(xiàn)需要3次磁盤I/O操作肿轨,和3次內(nèi)存查找操作,由于內(nèi)存中的關(guān)鍵字是一個有序表結(jié)構(gòu)蕊程,可以利用二分法快速定位到目標(biāo)數(shù)據(jù)椒袍,而3次磁盤I/O操作是影響整個B-Tree查找效率的決定因素。

B-樹相對于avl樹藻茂,通過在節(jié)點中增加節(jié)點內(nèi)部數(shù)據(jù)的個數(shù)來減少磁盤的io操作驹暑。

上面我們說過mysql是采用頁方式來讀寫數(shù)據(jù),每頁是16KB捌治,我們用B-樹來存儲mysql的記錄岗钩,每個節(jié)點對應(yīng)mysql中的一頁(16KB)纽窟,假如每行記錄加上樹節(jié)點中的1個指針占160Byte肖油,那么每個節(jié)點可以存儲1000(16KB/160byte)條數(shù)據(jù),樹的高度為3的節(jié)點大概可以存儲(第一層1000+第二層1000^2+第三層1000^3)10億條記錄臂港,是不是非常驚訝森枪,一個高度為3個B-樹大概可以存儲10億條記錄,我們從10億記錄中查找數(shù)據(jù)只需要3次io操作可以定位到目標(biāo)數(shù)據(jù)所在的頁审孽,而頁內(nèi)部的數(shù)據(jù)又是有序的县袱,然后將其加載到內(nèi)存中用二分法查找,是非秤恿Γ快的式散。

可以看出使用B-樹定位某個值還是很快的(10億數(shù)據(jù)中3次io操作+內(nèi)存中二分法),但是也是有缺點的:B-不利于范圍查找打颤,比如上圖中我們需要查找[15,36]區(qū)間的數(shù)據(jù)暴拄,需要訪問7個磁盤塊(1/2/7/3/8/4/9)漓滔,io次數(shù)又上去了,范圍查找也是我們經(jīng)常用到的乖篷,所以b-樹也不太適合在磁盤中存儲需要檢索的數(shù)據(jù)响驴。

b+樹

先看個b+樹結(jié)構(gòu)圖:

Mysql索引的詳解知識送給大家

b+樹的特征

每個結(jié)點至多有m個子女

除根結(jié)點外,每個結(jié)點至少有[m/2]個子女,根結(jié)點至少有兩個子女

有k個子女的結(jié)點必有k個關(guān)鍵字

父節(jié)點中持有訪問子節(jié)點的指針

父節(jié)點的關(guān)鍵字在子節(jié)點中都存在(如上面的1/20/35在每層都存在)撕蔼,要么是最小值豁鲤,要么是最大值,如果節(jié)點中關(guān)鍵字是升序的方式鲸沮,父節(jié)點的關(guān)鍵字是子節(jié)點的最小值

最底層的節(jié)點是葉子節(jié)點

除葉子節(jié)點之外琳骡,其他節(jié)點不保存數(shù)據(jù),只保存關(guān)鍵字和指針

葉子節(jié)點包含了所有數(shù)據(jù)的關(guān)鍵字以及data诉探,葉子節(jié)點之間用鏈表連接起來日熬,可以非常方便的支持范圍查找

b+樹與b-樹的幾點不同

b+樹中一個節(jié)點如果有k個關(guān)鍵字劳澄,最多可以包含k個子節(jié)點(k個關(guān)鍵字對應(yīng)k個指針)顾犹;而b-樹對應(yīng)k+1個子節(jié)點(多了一個指向子節(jié)點的指針)

b+樹除葉子節(jié)點之外其他節(jié)點值存儲關(guān)鍵字和指向子節(jié)點的指針,而b-樹還存儲了數(shù)據(jù)油昂,這樣同樣大小情況下敬肚,b+樹可以存儲更多的關(guān)鍵字

b+樹葉子節(jié)點中存儲了所有關(guān)鍵字及data毕荐,并且多個節(jié)點用鏈表連接,從上圖中看子節(jié)點中數(shù)據(jù)從左向右是有序的艳馒,這樣快速可以支撐范圍查找(先定位范圍的最大值和最小值憎亚,然后子節(jié)點中依靠鏈表遍歷范圍數(shù)據(jù))

B-Tree和B+Tree該如何選擇?

B-Tree因為非葉子結(jié)點也保存具體數(shù)據(jù)弄慰,所以在查找某個關(guān)鍵字的時候找到即可返回第美。而B+Tree所有的數(shù)據(jù)都在葉子結(jié)點,每次查找都得到葉子結(jié)點陆爽。所以在同樣高度的B-Tree和B+Tree中什往,B-Tree查找某個關(guān)鍵字的效率更高。

由于B+Tree所有的數(shù)據(jù)都在葉子結(jié)點慌闭,并且結(jié)點之間有指針連接别威,在找大于某個關(guān)鍵字或者小于某個關(guān)鍵字的數(shù)據(jù)的時候,B+Tree只需要找到該關(guān)鍵字然后沿著鏈表遍歷就可以了驴剔,而B-Tree還需要遍歷該關(guān)鍵字結(jié)點的根結(jié)點去搜索省古。

由于B-Tree的每個結(jié)點(這里的結(jié)點可以理解為一個數(shù)據(jù)頁)都存儲主鍵+實際數(shù)據(jù),而B+Tree非葉子結(jié)點只存儲關(guān)鍵字信息丧失,而每個頁的大小有限是有限的豺妓,所以同一頁能存儲的B-Tree的數(shù)據(jù)會比B+Tree存儲的更少。這樣同樣總量的數(shù)據(jù),B-Tree的深度會更大琳拭,增大查詢時的磁盤I/O次數(shù)载佳,進(jìn)而影響查詢效率。

Mysql的存儲引擎和索引

mysql內(nèi)部索引是由不同的引擎實現(xiàn)的臀栈,主要說一下InnoDB和MyISAM這兩種引擎中的索引蔫慧,這兩種引擎中的索引都是使用b+樹的結(jié)構(gòu)來存儲的。

InnoDB中的索引

Innodb中有2種索引:主鍵索引(聚集索引)权薯、輔助索引(非聚集索引)姑躲。

主鍵索引:每個表只有一個主鍵索引,葉子節(jié)點同時保存了主鍵的值也數(shù)據(jù)記錄盟蚣。

輔助索引:葉子節(jié)點保存了索引字段的值以及主鍵的值黍析。

MyISAM引擎中的索引

不管是主鍵索引還是輔助索引結(jié)構(gòu)都是一樣的,葉子節(jié)點保存了索引字段的值以及數(shù)據(jù)記錄的地址屎开。

如下圖:

有一張表阐枣,Id作為主索引,Name作為輔助索引奄抽。

Mysql索引的詳解知識送給大家

InnoDB數(shù)據(jù)檢索過程

如果需要查詢id=14的數(shù)據(jù)蔼两,只需要在左邊的主鍵索引中檢索就可以了。

如果需要搜索name='Ellison'的數(shù)據(jù)逞度,需要2步:

先在輔助索引中檢索到name='Ellison'的數(shù)據(jù)额划,獲取id為14

再到主鍵索引中檢索id為14的記錄

輔助索引這個查詢過程在mysql中叫做回表。

MyISAM數(shù)據(jù)檢索過程

在索引中找到對應(yīng)的關(guān)鍵字档泽,獲取關(guān)鍵字對應(yīng)的記錄的地址

通過記錄的地址查找到對應(yīng)的數(shù)據(jù)記錄

我們用的最多的是innodb存儲引擎俊戳,所以此處主要說一下innodb索引的情況,innodb中最好是采用主鍵查詢馆匿,這樣只需要一次索引抑胎,如果使用輔助索引檢索,涉及到回表操作渐北,比主鍵查詢要耗時一些阿逃。

innodb中輔助索引為什么不像myisam那樣存儲記錄的地址?

表中的數(shù)據(jù)發(fā)生變更的時候腔稀,會影響其他記錄地址的變化盆昙,如果輔助索引中記錄數(shù)據(jù)的地址羽历,此時會受影響焊虏,而主鍵的值一般是很少更新的,當(dāng)頁中的記錄發(fā)生地址變更的時候秕磷,對輔助索引是沒有影響的诵闭。

我們來看一下mysql中頁的結(jié)構(gòu),頁是真正存儲記錄的地方,對應(yīng)B+樹中的一個節(jié)點疏尿,也是mysql中讀寫數(shù)據(jù)的最小單位瘟芝,頁的結(jié)構(gòu)設(shè)計也是相當(dāng)有水平的,能夠加快數(shù)據(jù)的查詢褥琐。

頁結(jié)構(gòu)

mysql中頁是innodb中存儲數(shù)據(jù)的基本單位锌俱,也是mysql中管理數(shù)據(jù)的最小單位,和磁盤交互的時候都是以頁來進(jìn)行的敌呈,默認(rèn)是16kb贸宏,mysql中采用b+樹存儲數(shù)據(jù),頁相當(dāng)于b+樹中的一個節(jié)點磕洪。

頁的結(jié)構(gòu)如下圖:

Mysql索引的詳解知識送給大家

每個Page都有通用的頭和尾吭练,但是中部的內(nèi)容根據(jù)Page的類型不同而發(fā)生變化。Page的頭部里有我們關(guān)心的一些數(shù)據(jù)析显,下圖把Page的頭部詳細(xì)信息顯示出來:

Mysql索引的詳解知識送給大家

我們重點關(guān)注和數(shù)據(jù)組織結(jié)構(gòu)相關(guān)的字段:Page的頭部保存了兩個指針鲫咽,分別指向前一個Page和后一個Page,根據(jù)這兩個指針我們很容易想象出Page鏈接起來就是一個雙向鏈表的結(jié)構(gòu)谷异,如下圖:

Mysql索引的詳解知識送給大家

再看看Page的主體內(nèi)容分尸,我們主要關(guān)注行數(shù)據(jù)和索引的存儲,他們都位于Page的User Records部分歹嘹,User Records占據(jù)Page的大部分空間寓落,User Records由一條一條的Record組成。在一個Page內(nèi)部荞下,單鏈表的頭尾由固定內(nèi)容的兩條記錄來表示伶选,字符串形式的"Infimum"代表開頭,"Supremum"代表結(jié)尾尖昏,這兩個用來代表開頭結(jié)尾的Record存儲在System Records的仰税,Infinum、Supremum和User Records組成了一個單向鏈表結(jié)構(gòu)抽诉。最初數(shù)據(jù)是按照插入的先后順序排列的陨簇,但是隨著新數(shù)據(jù)的插入和舊數(shù)據(jù)的刪除,數(shù)據(jù)物理順序會變得混亂迹淌,但他們依然通過鏈表的方式保持著邏輯上的先后順序河绽,如下圖:

Mysql索引的詳解知識送給大家

把User Record的組織形式和若干Page組合起來,就看到了稍微完整的形式唉窃。

Mysql索引的詳解知識送給大家

innodb為了快速查找記錄耙饰,在頁中定義了一個稱之為page directory的目錄槽(slots),每個槽位占用兩個字節(jié)(用于保存指向記錄的地址),page directory中的多個slot組成了一個有序數(shù)組(可用于二分法快速定位記錄纹份,向下看)苟跪,行記錄被Page Directory邏輯的分成了多個塊廷痘,塊與塊之間是有序的,能夠加速記錄的查找件已,如下圖:

Mysql索引的詳解知識送給大家

看上圖笋额,每個行記錄的都有一個n_owned的區(qū)域(圖中粉色區(qū)域),n_owned標(biāo)識所屬的slot這個這個塊有多少條數(shù)據(jù)篷扩,偽記錄Infimum的n_owned值總是1兄猩,記錄Supremum的n_owned的取值范圍為[1,8],其他用戶記錄n_owned的取值范圍[4,8]鉴未,并且只有每個塊中最大的那條記錄的n_owned才會有值厦滤,其他的用戶記錄的n_owned為0。

數(shù)據(jù)檢索過程

在page中查詢數(shù)據(jù)的時候歼狼,先通過b+樹中查詢方法定位到數(shù)據(jù)所在的頁掏导,然后將頁內(nèi)整體加載到內(nèi)存中,通過二分法在page directory中檢索數(shù)據(jù)羽峰,縮小范圍趟咆,比如需要檢索7,通過二分法查找到7位于slot2和slot3所指向的記錄中間梅屉,然后從slot3指向的記錄5開始向后向后一個個找值纱,可以找到記錄7,如果里面沒有7坯汤,走到slot2向的記錄8結(jié)束虐唠。

n_owned范圍控制在[4,8]內(nèi),能保證每個slot管轄的范圍內(nèi)數(shù)據(jù)量控制在[4,8]個惰聂,能夠加速目標(biāo)數(shù)據(jù)的查找疆偿,當(dāng)有數(shù)據(jù)插入的時候,page directory為了控制每個slot對應(yīng)塊中記錄的個數(shù)([4,8])搓幌,此時page directory中會對slot的數(shù)量進(jìn)行調(diào)整杆故。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市溉愁,隨后出現(xiàn)的幾起案子处铛,更是在濱河造成了極大的恐慌,老刑警劉巖拐揭,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撤蟆,死亡現(xiàn)場離奇詭異,居然都是意外死亡堂污,警方通過查閱死者的電腦和手機家肯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敷鸦,“玉大人息楔,你說我怎么就攤上這事“桥” “怎么了值依?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碟案。 經(jīng)常有香客問我愿险,道長,這世上最難降的妖魔是什么价说? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任辆亏,我火速辦了婚禮,結(jié)果婚禮上鳖目,老公的妹妹穿的比我還像新娘扮叨。我一直安慰自己,他們只是感情好领迈,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布彻磁。 她就那樣靜靜地躺著,像睡著了一般狸捅。 火紅的嫁衣襯著肌膚如雪衷蜓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天尘喝,我揣著相機與錄音磁浇,去河邊找鬼。 笑死朽褪,一個胖子當(dāng)著我的面吹牛置吓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缔赠,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼交洗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了橡淑?” 一聲冷哼從身側(cè)響起构拳,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎梁棠,沒想到半個月后置森,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡符糊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年凫海,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片男娄。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡行贪,死狀恐怖漾稀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情建瘫,我是刑警寧澤崭捍,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站啰脚,受9級特大地震影響殷蛇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜橄浓,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一粒梦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧荸实,春花似錦匀们、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至圆存,卻和暖如春叼旋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沦辙。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工夫植, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人油讯。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓详民,卻偏偏與公主長得像,于是被迫代替她去往敵國和親陌兑。 傳聞我的和親對象是個殘疾皇子沈跨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容