本文主要以列表形式將B+樹的特色以及注意點等列出來,主要參考《算法導論》、維基百科、各大博客的內容,結合本身的理解寫的,如內容有不當之處,請各位雅正。
1.前言
B樹是爲磁盤或其餘直接存取的輔助存儲設備而設計的一種平衡搜索樹。B樹相似於紅黑樹,但它們在下降磁盤I/O操做數方面要更好一些。如今許多數據庫系統使用B樹或者B樹的變種(B+樹和B*樹)來存儲信息。B樹用的比較廣泛,許多書籍、博客都有詳細的介紹,對於B樹的嚴格定義也相對統一,在這裏就不予贅述。 B+樹是B樹的一種變形,它把全部的衛星數據都存儲在葉節點中,內部節點只存放關鍵字和孩子指針,所以最大化了內部節點的分支因子,因此B+樹的遍歷也更加高效(B樹須要以中序的方式遍歷節點,而B+樹只需把全部葉子節點串成鏈表就能夠從頭至尾遍歷)。
如下先放一張我所依據的B+樹的圖示(這張圖有所簡化,下面講完定義後會貼一張更加詳細的圖,兩圖本質並沒有差別):

2.定義
B+樹的定義我沒有找到官方的定義(若是有找到的人望告知我),有些定義在論壇還有爭議,可是這些並無多大影響,只是一點小小的差別,下面的定義中有涉及爭議的部分我會說起。
B+樹的定義以下:
每一個節點node有下面的屬性: n個關鍵字key[1],key[2], … ,key[n],以非降序存放,使得key[1]≤key[2]≤…≤key[n];
isRoot,一個布爾值,若是node是根節點,則爲TRUE;不然爲FALSE;
isLeaf,一個布爾值,若是node是葉子節點,則爲TRUE;不然爲FALSE;
Node*類型的parent指針,指向該節點的父節點
每一個內部節點還包含
n個
指向其孩子children[0],children[1], … , children[n],葉子節點沒有孩子。(注:此處有爭議,B+樹究竟是與B 樹n-1個關鍵字有n棵子樹保持一致,仍是B+樹n個關鍵字的結點中含有n棵子樹;兩種定義均可以,只要本身實現的時候統一用一種就行。如無特殊說明,如下的都是後者:
即n個關鍵字對應n棵子樹);
內部節點的關鍵字對存儲在各子樹中的關鍵字範圍加以分割:若是key[i]爲任意一個存儲在內部節點中的關鍵字,childNum[i]爲該節點的對應下標的子樹指針指向的節點的任意一個關鍵字,那麼
key[1] ≤ childNum[1] < key[2] ≤ childNum[2] < key[3] ≤ childNum[3] < … < key[n] ≤ childNum[n]
內部節點並不存儲真正的信息,而是保存其葉子節點的最小值做爲索引。好比下圖,標註1和標註2都是內部節點,裏面保存的並非真正的信息,而是標註3所示的節點中的最小值。(注:此處有爭議以最大值做爲索引,一樣也是不影響的爭議)

任何和關鍵字相聯繫的
「衛星數據(satellite information)」 將與關鍵字同樣存放在葉子節點中,通常地,可能只是爲每一個關鍵字對應的」衛星數據」存放一個指針,指針指向存放實際數據的磁盤頁,匹配了某個葉子節點的關鍵字便可經過該指針找到其餘對應數據。
每一個葉子節點還有指向下一個節點的指針next,方便遍歷整棵B+樹。
每一個葉子節點具備相同的深度,即樹的高度h。
每一個節點所包含的關鍵字個數有上界和下界,用一個被B+樹的
最小度數(minmum degree)的固定整數t≥2來表示這些界: 除了根節點之外的每一個節點必須至少有t個關鍵字。所以,除了根節點之外的每一個內部節點至少有t個孩子
每一個節點至多有2t個關鍵字,所以,一個內部節點至多可有2t個孩子。當一個節點剛好有2t個關鍵字時,稱該節點是
滿的。
結合以上的具體定義,下面這張圖更加詳細的描述了一棵具體的B+樹

3.注意點
在B+樹的學習與實現過程當中,也遇到很多的疑惑之處,現記錄以下,持續更新:
內部節點並不存儲真正的信息,而是保存其葉子節點的最小值做爲索引。每次插入刪除都進行更新(此時用到parent指針),保持最新狀態。
關於全部葉子節點都處於同一深度是如何實現的?這與B+樹具體的插入和刪除算法有關。簡單解釋一下插入時的狀況,根據插入值的大小,逐步向下直到對應的葉子節點。若是葉子節點關鍵字個數小於2t,則直接插入值或者更新衛星數據;若是插入以前葉子節點已經滿了,則分裂該葉子節點成兩半,並把中間值提上到父節點的關鍵字中,若是這致使父節點滿了的話,則把該父節點分裂,如此遞歸向上。因此樹高是一層層的增長的,葉子節點永遠都在同一深度。下面是我實現的B+樹中的插入代碼的片斷:
public void insert(Comparable key, Object obj, BPlusTree tree)
{
// 葉子節點則插入
if (isLeaf) {
// 不須要分裂直接插入
if (containsKeyword(key) || keywords.size() < tree.getDegree()) {
insertInNotFull(key, obj);
// 直接插入
if (parent != null) {
parent.updateAfterInsert(tree); // 更新父節點的信息(將最小的值存到父節點的關鍵字中做爲索引)
}
} else { // 須要分裂成左右兩個節點
splitNode(key, obj, tree);
}
} else { // 若是不是葉子節點則繼續往下搜索
Node leafNode = downToLeaf(key); // 逐步向下到對應的葉子節點
leafNode.insert(key, obj, tree);
}
}複製代碼
4.結語
B+樹還有一個最大的好處,方便掃庫,B樹必須用中序遍歷的方法按序掃庫,而B+樹直接從葉子結點挨個掃一遍就完了,B+樹支持range-query很是方便,而B樹不支持。這是數據庫選用B+樹的最主要緣由。
歡迎各位大牛批評指正。PS:我實現了一個小型B+樹系統,使用Java寫的,支持插入、搜索、遍歷B+樹,有須要的同窗能夠去下載。連接奉上:
B+樹實現(Java源碼)