水人的專欄
阿新 • • 發佈:2018-12-25
為什麼說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