B+樹

爲什麼說B+-tree比B 樹更適合實際應用中操作系統的文件索引和數據庫索引?

1) B+-tree的磁盤讀寫代價更低 
B+-tree的內部結點並沒有指向關鍵字具體信息的指針。因此其內部結點相對B 樹更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的需要查找的關鍵字也就越多。相對來說IO讀寫次數也就降低了。 
舉個例子,假設磁盤中的一個盤塊容納16bytes,而一個關鍵字2bytes,一個關鍵字具體信息指針2bytes。一棵9階B-tree(一個結點最多8個關鍵字)的內部結點需要2個盤快。而B+ 樹內部結點只需要1個盤快。當需要把內部結點讀入內存中的時候,B 樹就比B+ 樹多一次盤塊查找時間(在磁盤中就是盤片旋轉的時間)。
看節點定義可以看出來。
2) B+-tree的查詢效率更加穩定
由於非終結點並不是最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查找必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當。
3)B+樹還有一個最大的好處,方便掃庫,B樹必須用中序遍歷的方法按序掃庫,而B+樹直接從葉子結點挨個掃一遍就完了,B+樹支持range-query非常方便,而B樹不支持。這是數據庫選用B+樹的最主要原因。

“有很多基於頻率的搜索是選用B樹,越頻繁query的結點越往根上走,前提是需要對query做統計,而且要對key做一些變化。”

B*-tree:
B*-tree是B+-tree的變體,在B+ 樹非根和非葉子結點再增加指向兄弟的指針;B*樹定義了非葉子結點關鍵字個數至少爲(2/3)*M,即塊的最低使用率爲2/3(代替B+樹的1/2)。


B-tree:
定義:m階,子女節點個數從ceil(m/2)到m/2,關鍵字個數從ceil(m/2)-1到m-1,比子女節點個數剛好少1.
/**
 *  插入關鍵字僞代碼:
	1、找到應該插入位置的節點,一定是葉子節點,直接插入;
	2、如果該葉子節點關鍵字個數大於m-1;分裂該葉子節點;

	分裂節點僞代碼:
	1、分裂該節點,產生一個新節點;
	2、將中間關鍵字插入父節點中;
	3、如果父節點關鍵字個數大於m-1,遞歸分裂父節點,否則直接返回;

 *  如果name已經存在,返回false
 **/
/**
 *  刪除關鍵字僞代碼----最終是在葉子節點上刪除數據;
    1、查找包含這個關鍵字key的節點node:
	2、如果這個節點是內節點:
		a 找到左子樹中含有最大關鍵字的節點leafnode,及其最大的關鍵字keyx;
	    b 在葉子節點leafnode上刪除關鍵字keyx,並用keyx代替原來將要刪除的關鍵字key
		c 維護leafnode
	   如果這個節點是葉子節點:
	    a 直接刪除這個關鍵字,移動後面的其他關鍵字
		b 維護這個節點

	維護節點僞代碼:
		a 如果關鍵字滿足要求,直接返回;
		b 如果左右兄弟節點有足夠多的關鍵字,向其借一個,返回;
		c 如果左右兄弟節點都沒有足夠的關鍵字,合併一個兄弟節點,回溯維護父節點。
 *
 *  如果name已經存在,返回false
 **/

B+tree:
定義:內部節點全部是索引關鍵字,data都在葉子節點。
內部節點:子女節點個數爲ceil[m/2]到m,關鍵字可以和子女節點對應也可以少1(不同的書有不同的說法).關鍵字i是子樹i+1的最小關鍵字。
葉子節點:全部是key-value值,個數ceil(L/2)-->L( L << M in practice)。
根節點:單個或是2-->M個子節點。

實際中上:
每個節點通常佔用一個I/O塊;
1/2級節點常駐內存;
大多數情況很多內部節點的關鍵字個數少於(m-1),對內存的極大浪費。

各大IT公司面試常常問到的問題——海量數據問題。以前通常回答二級索引,即一級索引常駐內存,通過一級索引找到二級索引,讀入內存,再通過二級索引找到最終要找的具體數據,而“索引”,一直設想的都是HASH,現在回頭想來,HASH其實是不合適的。因爲HASH只能提供映射,而不能提供範圍信息。這個問題的正確答案應該是B樹或者B+樹。 

Insert()
/**
    1、找到葉子節點,直接插入;
    2、>L, 則SplitLeafNode 或者是 SplitRootNode()

    SplitLeafNode()
    1 Node ==> LeftNode( ceil(L/2) ) + Right( remaining );
    2 向父節點插入rightNode和right節點中最小的關鍵字 InsertInternalNode(key, rightNode)
    
    InsertInternalNode()
    1 直接插入關鍵字及其右子樹;
    2 子女節點個數>M,則 SplitInternalNode()

    SplitInternalNode()
    1 Node ==> Left( ceil(M/2)-1 ) + Key(中間) + Right( M/2 )
    2 InsertInternalNode( Key, Right )
 **/
Delete()
/**
    感覺挺麻煩的,細節還是不太清楚
    1、找到葉子節點,刪除,如果有內部節點包含這個關鍵字,則用葉子節點的最小值替換
    2、關鍵字個數小於<L:Rebalance();

    Rebalance()
    1、如果LeftSibling關鍵字個數>=ceil(L/2)+1,LendLeftSibling;
    2、LendRightSibling
    3、MergeLeafNode() or MergeInternalNode()
    
    MergeLeafNode: 注意parent的key只需要刪除就可以了
    MergeInternalNode:注意parent的key要往下移動
 **/

參考:http://blog.csdn.net/v_JULY_v/article/details/6530142
http://en.wikipedia.org/wiki/B%2Btree
一份B+樹ppt,很好,http://www.albertsong.com/read-174.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章