B+樹作為Mysql索引結(jié)構(gòu)的優(yōu)點(diǎn)

面試時(shí)候經(jīng)常會(huì)被問(wèn)到mysql的索引結(jié)構(gòu),B+樹相較二叉樹涛菠,紅黑樹的優(yōu)勢(shì)等問(wèn)題莉御,接下來(lái)就分析下這些問(wèn)題。

二叉查找樹(BST)

首先俗冻,讓我們先看一張圖:

1

從圖中可以看到礁叔,我們?yōu)?user 表(用戶信息表)建立了一個(gè)二叉查找樹的索引。

圖中的圓為二叉查找樹的節(jié)點(diǎn)迄薄,節(jié)點(diǎn)中存儲(chǔ)了鍵(key)和數(shù)據(jù)(data)琅关。鍵對(duì)應(yīng) user 表中的 id,數(shù)據(jù)對(duì)應(yīng) user 表中的行數(shù)據(jù)讥蔽。

二叉查找樹的特點(diǎn)就是任何節(jié)點(diǎn)的左子節(jié)點(diǎn)的鍵值都小于當(dāng)前節(jié)點(diǎn)的鍵值涣易,右子節(jié)點(diǎn)的鍵值都大于當(dāng)前節(jié)點(diǎn)的鍵值。頂端的節(jié)點(diǎn)我們稱為根節(jié)點(diǎn)冶伞,沒(méi)有子節(jié)點(diǎn)的節(jié)點(diǎn)我們稱之為葉節(jié)點(diǎn)新症。

如果我們需要查找 id=12 的用戶信息,利用我們創(chuàng)建的二叉查找樹索引响禽,查找流程如下:

  • 將根節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn)徒爹,把 12 與當(dāng)前節(jié)點(diǎn)的鍵值 10 比較荚醒,12 大于 10,接下來(lái)我們把當(dāng)前節(jié)點(diǎn)>的右子節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn)隆嗅。
  • 繼續(xù)把 12 和當(dāng)前節(jié)點(diǎn)的鍵值 13 比較腌且,發(fā)現(xiàn) 12 小于 13,把當(dāng)前節(jié)點(diǎn)的左子節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn)榛瓮。
  • 把 12 和當(dāng)前節(jié)點(diǎn)的鍵值 12 對(duì)比铺董,12 等于 12,滿足條件禀晓,我們從當(dāng)前節(jié)點(diǎn)中取出 data精续,即 id=12,name=xm粹懒。

利用二叉查找樹我們只需要 3 次即可找到匹配的數(shù)據(jù)重付。如果在表中一條條的查找的話,我們需要 6 次才能找到凫乖。

平衡二叉樹

上面我們講解了利用二叉查找樹可以快速的找到數(shù)據(jù)确垫。但是,如果上面的二叉查找樹是這樣的構(gòu)造:

2

這個(gè)時(shí)候可以看到我們的二叉查找樹變成了一個(gè)鏈表帽芽。如果我們需要查找 id=17 的用戶信息删掀,我們需要查找 7 次,也就相當(dāng)于全表掃描了导街。 導(dǎo)致這個(gè)現(xiàn)象的原因其實(shí)是二叉查找樹變得不平衡了披泪,也就是高度太高了,從而導(dǎo)致查找效率的不穩(wěn)定搬瑰。為了解決這個(gè)問(wèn)題款票,我們需要保證二叉查找樹一直保持平衡,就需要用到平衡二叉樹了泽论。 平衡二叉樹又稱 AVL 樹艾少,在滿足二叉查找樹特性的基礎(chǔ)上,要求每個(gè)節(jié)點(diǎn)的左右子樹的高度差不能超過(guò) 1翼悴。
下面是平衡二叉樹和非平衡二叉樹的對(duì)比:


3

由平衡二叉樹的構(gòu)造我們可以發(fā)現(xiàn)第一張圖中的二叉樹其實(shí)就是一棵平衡二叉樹缚够。

平衡二叉樹保證了樹的構(gòu)造是平衡的,當(dāng)我們插入或刪除數(shù)據(jù)導(dǎo)致不滿足平衡二叉樹不平衡時(shí)抄瓦,平衡二叉樹會(huì)進(jìn)行調(diào)整樹上的節(jié)點(diǎn)來(lái)保持平衡潮瓶。具體的調(diào)整方式這里就不介紹了陶冷。平衡二叉樹相比于二叉查找樹來(lái)說(shuō)钙姊,查找效率更穩(wěn)定,總體的查找速度也更快埂伦。

B樹

因?yàn)閮?nèi)存的易失性煞额。一般情況下,我們都會(huì)選擇將 user 表中的數(shù)據(jù)和索引存儲(chǔ)在磁盤這種外圍設(shè)備中。但是和內(nèi)存相比膊毁,從磁盤中讀取數(shù)據(jù)的速度會(huì)慢上百倍千倍甚至萬(wàn)倍胀莹,所以,我們應(yīng)當(dāng)盡量減少?gòu)拇疟P中讀取數(shù)據(jù)的次數(shù)婚温。另外描焰,從磁盤中讀取數(shù)據(jù)時(shí),都是按照磁盤塊來(lái)讀取的栅螟,并不是一條一條的讀荆秦。如果我們能把盡量多的數(shù)據(jù)放進(jìn)磁盤塊中,那一次磁盤讀取操作就會(huì)讀取更多數(shù)據(jù)力图,那我們查找數(shù)據(jù)的時(shí)間也會(huì)大幅度降低步绸。如果我們用樹這種數(shù)據(jù)結(jié)構(gòu)作為索引的數(shù)據(jù)結(jié)構(gòu),那我們每查找一次數(shù)據(jù)就需要從磁盤中讀取一個(gè)節(jié)點(diǎn),也就是我們說(shuō)的一個(gè)磁盤塊。我們都知道平衡二叉樹可是每個(gè)節(jié)點(diǎn)只存儲(chǔ)一個(gè)鍵值和數(shù)據(jù)的疆栏。那說(shuō)明什么快耿?說(shuō)明每個(gè)磁盤塊僅僅存儲(chǔ)一個(gè)鍵值和數(shù)據(jù)!那如果我們要存儲(chǔ)海量的數(shù)據(jù)呢棕硫?

可以想象到二叉樹的節(jié)點(diǎn)將會(huì)非常多,高度也會(huì)極其高,我們查找數(shù)據(jù)時(shí)也會(huì)進(jìn)行很多次磁盤 IO漾月,我們查找數(shù)據(jù)的效率將會(huì)極低!

4

為了解決平衡二叉樹的這個(gè)弊端胃珍,我們應(yīng)該尋找一種單個(gè)節(jié)點(diǎn)可以存儲(chǔ)多個(gè)鍵值和數(shù)據(jù)的平衡樹梁肿。也就是我們接下來(lái)要說(shuō)的 B 樹。

B 樹(Balance Tree)即為平衡樹的意思觅彰,下圖即是一棵 B 樹:

5

圖中的 p 節(jié)點(diǎn)為指向子節(jié)點(diǎn)的指針吩蔑,二叉查找樹和平衡二叉樹其實(shí)也有,因?yàn)閳D的美觀性填抬,被省略了烛芬。

圖中的每個(gè)節(jié)點(diǎn)稱為頁(yè),頁(yè)就是我們上面說(shuō)的磁盤塊飒责,在 MySQL 中數(shù)據(jù)讀取的基本單位都是頁(yè)赘娄,所以我們這里叫做頁(yè)更符合 MySQL 中索引的底層數(shù)據(jù)結(jié)構(gòu)。

從上圖可以看出宏蛉,B 樹相對(duì)于平衡二叉樹遣臼,每個(gè)節(jié)點(diǎn)存儲(chǔ)了更多的鍵值(key)和數(shù)據(jù)(data),并且每個(gè)節(jié)點(diǎn)擁有更多的子節(jié)點(diǎn)拾并,子節(jié)點(diǎn)的個(gè)數(shù)一般稱為階揍堰,上述圖中的 B 樹為 3 階 B 樹鹏浅,高度也會(huì)很低。

基于這個(gè)特性屏歹,B 樹查找數(shù)據(jù)讀取磁盤的次數(shù)將會(huì)很少隐砸,數(shù)據(jù)的查找效率也會(huì)比平衡二叉樹高很多。

假如我們要查找 id=28 的用戶信息蝙眶,那么我們?cè)谏蠄D B 樹中查找的流程如下:

  • 先找到根節(jié)點(diǎn)也就是頁(yè) 1季希,判斷 28 在鍵值 17 和 35 之間,那么我們根據(jù)頁(yè) 1 中的指針 p2 找到頁(yè) 3幽纷。
  • 將 28 和頁(yè) 3 中的鍵值相比較胖眷,28 在 26 和 30 之間,我們根據(jù)頁(yè) 3 中的指針 p2 找到頁(yè) 8霹崎。
  • 將 28 和頁(yè) 8 中的鍵值相比較珊搀,發(fā)現(xiàn)有匹配的鍵值 28,鍵值 28 對(duì)應(yīng)的用戶信息為(28尾菇,bv)境析。

B+樹

B+ 樹是對(duì) B 樹的進(jìn)一步優(yōu)化。讓我們先來(lái)看下 B+ 樹的結(jié)構(gòu)圖:

6

根據(jù)上圖我們來(lái)看下 B+ 樹和 B 樹有什么不同:

  1. B+ 樹非葉子節(jié)點(diǎn)上是不存儲(chǔ)數(shù)據(jù)的派诬,僅存儲(chǔ)鍵值劳淆,而 B 樹節(jié)點(diǎn)中不僅存儲(chǔ)鍵值,也會(huì)存儲(chǔ)數(shù)據(jù)默赂。
    之所以這么做是因?yàn)樵跀?shù)據(jù)庫(kù)中頁(yè)的大小是固定的沛鸵,InnoDB 中頁(yè)的默認(rèn)大小是 16KB。
    如果不存儲(chǔ)數(shù)據(jù)缆八,那么就會(huì)存儲(chǔ)更多的鍵值曲掰,相應(yīng)的樹的階數(shù)(節(jié)點(diǎn)的子節(jié)點(diǎn)樹)就會(huì)更大,樹就會(huì)更矮更胖奈辰,如此一來(lái)我們查找數(shù)據(jù)進(jìn)行磁盤的 IO 次數(shù)又會(huì)再次減少栏妖,數(shù)據(jù)查詢的效率也會(huì)更快。
    另外奖恰,B+ 樹的階數(shù)是等于鍵值的數(shù)量的吊趾,如果我們的 B+ 樹一個(gè)節(jié)點(diǎn)可以存儲(chǔ) 1000 個(gè)鍵值,那么 3 層 B+ 樹可以存儲(chǔ) 1000×1000×1000=10 億個(gè)數(shù)據(jù)瑟啃。
    一般根節(jié)點(diǎn)是常駐內(nèi)存的论泛,所以一般我們查找 10 億數(shù)據(jù),只需要 2 次磁盤 IO蛹屿。

  2. 因?yàn)?B+ 樹索引的所有數(shù)據(jù)均存儲(chǔ)在葉子節(jié)點(diǎn)屁奏,而且數(shù)據(jù)是按照順序排列的。
    那么 B+ 樹使得范圍查找蜡峰,排序查找了袁,分組查找以及去重查找變得異常簡(jiǎn)單。而 B 樹因?yàn)閿?shù)據(jù)分散在各個(gè)節(jié)點(diǎn)湿颅,要實(shí)現(xiàn)這一點(diǎn)是很不容易的载绿。
    有心的讀者可能還發(fā)現(xiàn)上圖 B+ 樹中各個(gè)頁(yè)之間是通過(guò)雙向鏈表連接的,葉子節(jié)點(diǎn)中的數(shù)據(jù)是通過(guò)單向鏈表連接的油航。
    其實(shí)上面的 B 樹我們也可以對(duì)各個(gè)節(jié)點(diǎn)加上鏈表崭庸。這些不是它們之前的區(qū)別,是因?yàn)樵?MySQL 的 InnoDB 存儲(chǔ)引擎中谊囚,索引就是這樣存儲(chǔ)的怕享。
    也就是說(shuō)上圖中的 B+ 樹索引就是 InnoDB 中 B+ 樹索引真正的實(shí)現(xiàn)方式,準(zhǔn)確的說(shuō)應(yīng)該是聚集索引镰踏。

通過(guò)上圖可以看到函筋,在 InnoDB 中,我們通過(guò)數(shù)據(jù)頁(yè)之間通過(guò)雙向鏈表連接以及葉子節(jié)點(diǎn)中數(shù)據(jù)之間通過(guò)單向鏈表連接的方式可以找到表中所有的數(shù)據(jù)奠伪。

MyISAM 中的 B+ 樹索引實(shí)現(xiàn)與 InnoDB 中的略有不同跌帐。在 MyISAM 中,B+ 樹索引的葉子節(jié)點(diǎn)并不存儲(chǔ)數(shù)據(jù)绊率,而是存儲(chǔ)數(shù)據(jù)的文件地址谨敛。

總結(jié)

  1. 二叉查找樹(BST):解決了排序的基本問(wèn)題,但是由于無(wú)法保證平衡滤否,可能退化為鏈表脸狸;

  2. 平衡二叉樹(AVL):通過(guò)旋轉(zhuǎn)解決了平衡的問(wèn)題,但是旋轉(zhuǎn)操作效率太低藐俺;

  3. B樹:通過(guò)將二叉樹改為多路平衡查找樹炊甲,解決了樹過(guò)高的問(wèn)題;

  4. B+樹:在B樹的基礎(chǔ)上欲芹,將非葉節(jié)點(diǎn)改造為不存儲(chǔ)數(shù)據(jù)的純索引節(jié)點(diǎn)蜜葱,進(jìn)一步降低了樹的高度;此外將葉節(jié)點(diǎn)使用指針連接成鏈表耀石,范圍查詢更加高效牵囤。

摘自:http://www.liuzk.com/410.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市滞伟,隨后出現(xiàn)的幾起案子揭鳞,更是在濱河造成了極大的恐慌,老刑警劉巖梆奈,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件野崇,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡亩钟,警方通過(guò)查閱死者的電腦和手機(jī)乓梨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門鳖轰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人扶镀,你說(shuō)我怎么就攤上這事蕴侣。” “怎么了臭觉?”我有些...
    開(kāi)封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵昆雀,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蝠筑,道長(zhǎng)狞膘,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任什乙,我火速辦了婚禮挽封,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘臣镣。我一直安慰自己场仲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布退疫。 她就那樣靜靜地躺著渠缕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪褒繁。 梳的紋絲不亂的頭發(fā)上亦鳞,一...
    開(kāi)封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音棒坏,去河邊找鬼燕差。 笑死,一個(gè)胖子當(dāng)著我的面吹牛坝冕,可吹牛的內(nèi)容都是我干的徒探。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼喂窟,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼测暗!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起磨澡,我...
    開(kāi)封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤碗啄,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后稳摄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稚字,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胆描。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘫想。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖昌讲,靈堂內(nèi)的尸體忽然破棺而出国夜,到底是詐尸還是另有隱情,我是刑警寧澤剧蚣,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布支竹,位于F島的核電站旋廷,受9級(jí)特大地震影響鸠按,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜饶碘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一目尖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扎运,春花似錦瑟曲、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至负拟,卻和暖如春烦衣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背掩浙。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工花吟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人厨姚。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓衅澈,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親谬墙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子今布,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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