B/B+ 樹(shù)及數(shù)據(jù)庫(kù)索引應(yīng)用

B-樹(shù)

定義:B-樹(shù)是一類(lèi)樹(shù)谁撼,包括 B-樹(shù)、B+樹(shù)滋饲、B* 樹(shù)等厉碟,是一棵自平衡的搜索樹(shù),它類(lèi)似普通的平衡二叉樹(shù)屠缭,不同的一點(diǎn)是 B-樹(shù)允許每個(gè)節(jié)點(diǎn)有更多的子節(jié)點(diǎn)箍鼓。B-樹(shù)是專(zhuān)門(mén)為外部存儲(chǔ)器設(shè)計(jì)的,如磁盤(pán)呵曹,它對(duì)于讀取和寫(xiě)入大塊數(shù)據(jù)有良好的性能款咖,所以一般被用在文件系統(tǒng)及數(shù)據(jù)庫(kù)中何暮。

為什么會(huì)出現(xiàn) B-樹(shù)這類(lèi)數(shù)據(jù)結(jié)構(gòu)?
  傳統(tǒng)用來(lái)搜索的平衡二叉樹(shù)有很多铐殃,如 AVL 樹(shù)海洼,紅黑樹(shù)等。這些樹(shù)在一般情況下查詢(xún)性能非常好背稼,但當(dāng)數(shù)據(jù)非常大的時(shí)候它們就無(wú)能為力了贰军。
  原因是當(dāng)數(shù)據(jù)量非常大時(shí),內(nèi)存不夠用蟹肘,大部分?jǐn)?shù)據(jù)只能存放在磁盤(pán)上词疼,只有需要的數(shù)據(jù)才加載到內(nèi)存中。一般而言?xún)?nèi)存訪(fǎng)問(wèn)的時(shí)間約為 50ns帘腹,而磁盤(pán)在 10ms 左右贰盗,相差了近 5 個(gè)數(shù)量級(jí)。這說(shuō)明程序大部分時(shí)間會(huì)阻塞在磁盤(pán) IO 上阳欲。
  那么我們?nèi)绾翁岣叱绦蛐阅芏嬗繙p少磁盤(pán) IO 次數(shù)。像 AVL 樹(shù)球化,紅黑樹(shù)這類(lèi)平衡二叉樹(shù)從設(shè)計(jì)上無(wú)法“迎合”磁盤(pán)秽晚。
  平衡二叉樹(shù)是通過(guò)旋轉(zhuǎn)來(lái)保持平衡的,而旋轉(zhuǎn)是對(duì)整棵樹(shù)的操作筒愚,若部分加載到內(nèi)存中則無(wú)法完成旋轉(zhuǎn)操作赴蝇。其次平衡二叉樹(shù)的高度相對(duì)較大為 log2n,這樣邏輯上很近的節(jié)點(diǎn)實(shí)際可能非常遠(yuǎn)巢掺,無(wú)法很好的利用磁盤(pán)預(yù)讀(局部性原理)句伶,所以這類(lèi)平衡二叉樹(shù)在數(shù)據(jù)庫(kù)和文件系統(tǒng)上的選擇就被 pass 了。

空間局部性原理:如果一個(gè)存儲(chǔ)器的某個(gè)位置被訪(fǎng)問(wèn)陆淀,那么將它附近的位置也會(huì)被訪(fǎng)問(wèn)考余。

從“迎合”磁盤(pán)的角度來(lái)看看 B-樹(shù)的設(shè)計(jì)
  索引的原理其實(shí)是不斷的縮小查找范圍轧苫,就如我們平時(shí)用字典查單詞一樣楚堤,先找首字母縮小范圍,再第二個(gè)字母等等浸剩。平衡二叉樹(shù)是每次將范圍分割為兩個(gè)區(qū)間钾军。為了更快,B-樹(shù)每次將范圍分割為多個(gè)區(qū)間绢要,區(qū)間越多,定位數(shù)據(jù)越快越精確拗小。
  那么如果節(jié)點(diǎn)為區(qū)間范圍重罪,每個(gè)節(jié)點(diǎn)就較大了。所以新建節(jié)點(diǎn)時(shí),直接申請(qǐng)頁(yè)大小的空間(磁盤(pán)是按 block 分的剿配,一般為 512 Byte搅幅。磁盤(pán) IO 一次讀取若干個(gè) block,我們稱(chēng)為一頁(yè)呼胚,具體大小和操作系統(tǒng)有關(guān)茄唐,一般為 4k,8k或 16k)蝇更,計(jì)算機(jī)內(nèi)存分配是按頁(yè)對(duì)齊的沪编,這樣就實(shí)現(xiàn)了一個(gè)節(jié)點(diǎn)只需要一次 IO。

上圖是一棵簡(jiǎn)化的 B-樹(shù)年扩,多叉的好處非常明顯蚁廓,有效的降低了 B-樹(shù)的高度,為底數(shù)很大的 logn厨幻。一般一棵 B-樹(shù)的高度在 3 層左右相嵌。
B-樹(shù)的每個(gè)節(jié)點(diǎn)是 n 個(gè)有序的序列(a1, a2, a3, … , an),并將該節(jié)點(diǎn)的子節(jié)點(diǎn)分割成 n+1 個(gè)區(qū)間來(lái)進(jìn)行索引(X1< a1 < X2 < a2 < … < an < Xn+1)况脆。

一個(gè) m 階的 B-樹(shù)滿(mǎn)足以下條件

  1. 有 k 棵子樹(shù)的分支結(jié)點(diǎn)存在 k-1 個(gè)關(guān)鍵碼饭宾,關(guān)鍵碼按照遞增次序進(jìn)行排列;
  2. 根結(jié)點(diǎn)至少擁有兩顆子樹(shù)(存在子樹(shù)的情況下)格了,至多擁有 m 棵子樹(shù)看铆;
  3. 除了根結(jié)點(diǎn)以外,每個(gè)結(jié)點(diǎn)至多擁有 m 棵子樹(shù)笆搓,其余每個(gè)分支結(jié)點(diǎn)至少擁有 m/2 棵子樹(shù)(即關(guān)鍵字?jǐn)?shù)量 n 需要滿(mǎn)足 ?m/2?-1 ≤ n ≤ m-1)性湿;
  4. 所有的葉結(jié)點(diǎn)都在同一層;

B-樹(shù)的查找
我們來(lái)看看 B-樹(shù)的查找满败,假設(shè)每個(gè)節(jié)點(diǎn)有 n 個(gè) 關(guān)鍵字肤频,被分割為 n+1 個(gè)區(qū)間,注意算墨,每個(gè) 關(guān)鍵字緊跟著 data 域宵荒,這說(shuō)明 B-樹(shù)的關(guān)鍵字和 data 是聚合在一起的。一般而言净嘀,根節(jié)點(diǎn)在內(nèi)存中报咳,B-樹(shù)以每個(gè)節(jié)點(diǎn)為一次磁盤(pán) IO,比如上圖中挖藏,若搜索關(guān)鍵字為 25 節(jié)點(diǎn)的 data暑刃,首先在根節(jié)點(diǎn)進(jìn)行二分查找(因?yàn)?keys 有序,二分最快)膜眠,判斷關(guān)鍵字 25 小于關(guān)鍵字 50岩臣,所以定位到最左側(cè)的節(jié)點(diǎn)溜嗜,此時(shí)進(jìn)行一次磁盤(pán) IO,將該節(jié)點(diǎn)從磁盤(pán)讀入內(nèi)存架谎,接著繼續(xù)進(jìn)行上述過(guò)程炸宵,直到找到該關(guān)鍵字為止。

一個(gè)酷炫的網(wǎng)頁(yè)谷扣,可以自己插入刪除節(jié)點(diǎn)土全,觀察 B-樹(shù)的變化情況:
https://www.cs.usfca.edu/~galles/visualization/BTree.html

B-樹(shù)的插入規(guī)則
新結(jié)點(diǎn)一般插入在最底層,通過(guò)搜索找到對(duì)應(yīng)的結(jié)點(diǎn)進(jìn)行插入会涎,根據(jù)即將插入的結(jié)點(diǎn)的關(guān)鍵字?jǐn)?shù)量又分為下面幾種情況:

  • 如果該結(jié)點(diǎn)的關(guān)鍵字個(gè)數(shù)沒(méi)有到達(dá) m-1 個(gè)裹匙,那么直接插入即可;
  • 如果該結(jié)點(diǎn)的關(guān)鍵字個(gè)數(shù)已到達(dá)了 m-1 個(gè)在塔,無(wú)法滿(mǎn)足 B-樹(shù)的性質(zhì)幻件,需要將其進(jìn)行分裂。分裂的規(guī)則是該結(jié)點(diǎn)分成兩半蛔溃,將中間的關(guān)鍵字提升到父親結(jié)點(diǎn)中绰沥,這又可能導(dǎo)致父節(jié)點(diǎn)的分裂,那就繼續(xù)向上回溯(甚至是要對(duì)根結(jié)點(diǎn)進(jìn)行分裂贺待,那么整棵樹(shù)都加了一層)徽曲。

過(guò)程如下:

B-樹(shù)的刪除規(guī)則
先通過(guò)搜索找到相應(yīng)的值,存在則進(jìn)行刪除:

  • 如果該結(jié)點(diǎn)擁有關(guān)鍵字?jǐn)?shù)量仍然大于或等于 ?m/2?-1麸塞,則不做任何處理秃臣;
  • 如果該結(jié)點(diǎn)在刪除關(guān)鍵字以后,關(guān)鍵字?jǐn)?shù)量小于 ?m/2?-1哪工,則需要向兄弟結(jié)點(diǎn)借關(guān)鍵字奥此,這又分為兄弟結(jié)點(diǎn)的關(guān)鍵字?jǐn)?shù)量是否足夠的情況:
    • 如果兄弟結(jié)點(diǎn)借出一個(gè)關(guān)鍵字仍滿(mǎn)足 B-樹(shù)性質(zhì),則將該節(jié)點(diǎn)與兄弟節(jié)點(diǎn)之間夾的父親結(jié)點(diǎn)關(guān)鍵字下移雁比,兄弟結(jié)點(diǎn)的關(guān)鍵字上移稚虎;
    • 如果兄弟結(jié)點(diǎn)的關(guān)鍵字在借出以后無(wú)法滿(mǎn)足情況,那么我們可以將該結(jié)點(diǎn)合并到兄弟結(jié)點(diǎn)中偎捎,合并之后的子結(jié)點(diǎn)數(shù)量少了一個(gè)蠢终,則需要將父親結(jié)點(diǎn)的關(guān)鍵字下放,如果父親結(jié)點(diǎn)不滿(mǎn)足性質(zhì)茴她,繼續(xù)向上回溯寻拂;

過(guò)程如下:

B+樹(shù)

B+樹(shù)是 B-樹(shù)的變種,它與 B-樹(shù)的不同之處在于:

  1. 在 B+樹(shù)中丈牢,關(guān)鍵字的副本存儲(chǔ)在內(nèi)部節(jié)點(diǎn)祭钉,真正的關(guān)鍵字和 data 存儲(chǔ)在葉子節(jié)點(diǎn)上(所有的 data 都在最后一層)。
  2. n 個(gè)關(guān)鍵字的節(jié)點(diǎn)指針域?yàn)?n 而不是 n+1己沛。

每個(gè)節(jié)點(diǎn)關(guān)鍵字的數(shù)量范圍依然是 ?m/2?-1 ~ m-1朴皆,順序遞增帕识。

因?yàn)閮?nèi)節(jié)點(diǎn)并不存儲(chǔ) data泛粹,所以一般 B+樹(shù)的葉節(jié)點(diǎn)和內(nèi)節(jié)點(diǎn)大小不同遂铡。為了增加區(qū)間訪(fǎng)問(wèn)性,一般會(huì)對(duì) B+樹(shù)做一些優(yōu)化晶姊。如下圖帶順序訪(fǎng)問(wèn)的 B+樹(shù):

B+樹(shù)的插入刪除類(lèi)似于 B-樹(shù)扒接。

B-樹(shù)和 B+樹(shù)的區(qū)別

1、B+樹(shù)內(nèi)節(jié)點(diǎn)不存儲(chǔ)數(shù)據(jù)们衙,所有 data 存儲(chǔ)在葉節(jié)點(diǎn)導(dǎo)致查詢(xún)時(shí)間復(fù)雜度固定為 logn钾怔。而 B-樹(shù)查詢(xún)時(shí)間復(fù)雜度不固定,與關(guān)鍵字在樹(shù)中的位置有關(guān)蒙挑,最好為 O(1)宗侦。

如下所示 B-樹(shù)/B+樹(shù)查詢(xún)關(guān)鍵字為 50 的 data。

B-樹(shù)

關(guān)鍵字為 50 的節(jié)點(diǎn)就在第一層忆蚀,B-樹(shù)只需要一次磁盤(pán) IO 即可完成查找矾利。


B+樹(shù)

由于 B+樹(shù)所有的 data 域都在根節(jié)點(diǎn),所以查詢(xún)關(guān)鍵字為 50 的節(jié)點(diǎn)必須從根節(jié)點(diǎn)索引到葉節(jié)點(diǎn)馋袜,時(shí)間復(fù)雜度固定為 O(logn)男旗。


2、B+樹(shù)葉節(jié)點(diǎn)兩兩相連可大大增加區(qū)間訪(fǎng)問(wèn)性欣鳖,可使用在范圍查詢(xún)等察皇,而 B-樹(shù)每個(gè)節(jié)點(diǎn)關(guān)鍵字和 data 在一起,則無(wú)法區(qū)間查找泽台。

根據(jù)空間局部性原理:如果一個(gè)存儲(chǔ)器的某個(gè)位置被訪(fǎng)問(wèn)什荣,那么將它附近的位置也會(huì)被訪(fǎng)問(wèn)。

B+樹(shù)可以很好的利用局部性原理怀酷,若我們?cè)L問(wèn)節(jié)點(diǎn)關(guān)鍵字為 50稻爬,則關(guān)鍵字為 55、60胰坟、62 的節(jié)點(diǎn)將來(lái)也可能被訪(fǎng)問(wèn)因篇,我們可以利用磁盤(pán)預(yù)讀原理提前將這些數(shù)據(jù)讀入內(nèi)存,減少了磁盤(pán) IO 的次數(shù)笔横。
當(dāng)然 B+樹(shù)也能夠很好的完成范圍查詢(xún)竞滓。比如查詢(xún)關(guān)鍵字在 50~70 之間的節(jié)點(diǎn)。


3吹缔、B+樹(shù)更適合外部存儲(chǔ)商佑。由于內(nèi)節(jié)點(diǎn)無(wú) data 域,每個(gè)節(jié)點(diǎn)能索引的范圍更大更精確厢塘。
由于 B-樹(shù)節(jié)點(diǎn)內(nèi)部每個(gè)關(guān)鍵字都帶著 data 域茶没,而 B+樹(shù)節(jié)點(diǎn)只存儲(chǔ) key 的副本肌幽,真實(shí)的關(guān)鍵字和 data 域都在葉子節(jié)點(diǎn)存儲(chǔ)。前面說(shuō)過(guò)磁盤(pán)是分 block 的抓半,一次磁盤(pán) IO 會(huì)讀取若干個(gè) block喂急,具體和操作系統(tǒng)有關(guān),那么由于磁盤(pán) IO 數(shù)據(jù)大小是固定的笛求,這就意味著B+樹(shù)單次磁盤(pán) IO 的信息量大于 B-樹(shù)廊移,從這點(diǎn)來(lái)看 B+樹(shù)相對(duì) B-樹(shù)磁盤(pán) IO 次數(shù)少。

為什么 MongoDB 索引選擇 B-樹(shù)探入,而 Mysql(InnoDB 引擎)索引選擇 B+樹(shù)

MongoDB 是文檔型的數(shù)據(jù)庫(kù)狡孔,是一種 NoSQL,一般使用 XML 或 Json 格式來(lái)保存數(shù)據(jù)蜂嗽,歸屬于聚合型數(shù)據(jù)庫(kù)苗膝。被設(shè)計(jì)用在數(shù)據(jù)模型簡(jiǎn)單,性能要求高的場(chǎng)合植旧。MongoDB 是聚合型數(shù)據(jù)庫(kù)(通常就是鍵值對(duì))辱揭,而 B-樹(shù)恰好關(guān)鍵字和 data 域聚合在一起。
Mysql 是一種關(guān)系型數(shù)據(jù)庫(kù)隆嗅,區(qū)間訪(fǎng)問(wèn)是常見(jiàn)的一種情況界阁,而 B-樹(shù)并不支持區(qū)間訪(fǎng)問(wèn),B+樹(shù)由于數(shù)據(jù)全部存儲(chǔ)在葉子節(jié)點(diǎn)胖喳,并且通過(guò)指針串在一起泡躯,這樣就很容易的進(jìn)行區(qū)間遍歷甚至全部遍歷,且每個(gè)節(jié)點(diǎn)能索引的范圍更大更精確丽焊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末较剃,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子技健,更是在濱河造成了極大的恐慌写穴,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雌贱,死亡現(xiàn)場(chǎng)離奇詭異啊送,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)欣孤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)馋没,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人降传,你說(shuō)我怎么就攤上這事篷朵。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵声旺,是天一觀的道長(zhǎng)笔链。 經(jīng)常有香客問(wèn)我,道長(zhǎng)腮猖,這世上最難降的妖魔是什么鉴扫? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮缚够,結(jié)果婚禮上幔妨,老公的妹妹穿的比我還像新娘。我一直安慰自己谍椅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布古话。 她就那樣靜靜地躺著雏吭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陪踩。 梳的紋絲不亂的頭發(fā)上杖们,一...
    開(kāi)封第一講書(shū)人閱讀 51,737評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音肩狂,去河邊找鬼摘完。 笑死,一個(gè)胖子當(dāng)著我的面吹牛傻谁,可吹牛的內(nèi)容都是我干的孝治。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼审磁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谈飒!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起态蒂,我...
    開(kāi)封第一講書(shū)人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤杭措,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后钾恢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體手素,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年瘩蚪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了泉懦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡募舟,死狀恐怖祠斧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拱礁,我是刑警寧澤琢锋,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布辕漂,位于F島的核電站,受9級(jí)特大地震影響吴超,放射性物質(zhì)發(fā)生泄漏钉嘹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一鲸阻、第九天 我趴在偏房一處隱蔽的房頂上張望跋涣。 院中可真熱鬧,春花似錦鸟悴、人聲如沸陈辱。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沛贪。三九已至,卻和暖如春震贵,著一層夾襖步出監(jiān)牢的瞬間利赋,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工猩系, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留媚送,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓寇甸,卻偏偏與公主長(zhǎng)得像塘偎,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子幽纷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

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

  • B樹(shù)的定義 一棵m階的B樹(shù)滿(mǎn)足下列條件: 樹(shù)中每個(gè)結(jié)點(diǎn)至多有m個(gè)孩子式塌。 除根結(jié)點(diǎn)和葉子結(jié)點(diǎn)外,其它每個(gè)結(jié)點(diǎn)至少有m...
    文檔隨手記閱讀 13,230評(píng)論 0 25
  • B樹(shù) 1.前言: 動(dòng)態(tài)查找樹(shù)主要有:二叉查找樹(shù)(Binary Search Tree)友浸,平衡二叉查找樹(shù)(Balan...
    鐵甲依然在_978f閱讀 1,447評(píng)論 0 4
  • 原文鏈接 B樹(shù) 1.前言: 動(dòng)態(tài)查找樹(shù)主要有:二叉查找樹(shù)(Binary Search Tree)峰尝,平衡二叉查找樹(shù)(...
    非典型程序員閱讀 1,162評(píng)論 0 3
  • 2017年5月11日 晴 憶起兒時(shí)的艱苦,咱家屬于超生游擊隊(duì)收恢,理所當(dāng)然的就成為了全村最窮的貧困戶(hù)武学,媽媽從小只...
    東尼日記閱讀 177評(píng)論 0 0
  • 1 說(shuō)起寫(xiě)文章火窒,已經(jīng)很久沒(méi)有寫(xiě)了,碎片化時(shí)間太多驮肉,寫(xiě)不出來(lái)熏矿,還記得15年的時(shí)候堅(jiān)持過(guò)一段時(shí)間,還是放棄了,多了真的...
    俊陽(yáng)中醫(yī)康復(fù)閱讀 279評(píng)論 0 0