各種樹形結構


1 搜索二叉樹:每個節點有兩個子節點,數據量的增大必然導致高度的快速增加,顯然這個不適合作為大量數據存儲的基礎結構。

  2 B樹:一棵m階B樹是一棵平衡的m路搜索樹。最重要的性質是每個非根節點所包含的關鍵字個數 j 滿足:┌m/2┐ - 1 <= j <= m - 1;一個節點的子節點數量會比關鍵字個數多1,這樣關鍵字就變成了子節點的分割標志。一般會在圖示中把關鍵字畫到子節點中間,非常形象,也容易和后面的B+樹區分。由於數據同時存在於葉子節點和非葉子結點中,無法簡單完成按順序遍歷B樹中的關鍵字,必須用中序遍歷的方法。

  3 B+樹:一棵m階B樹是一棵平衡的m路搜索樹。最重要的性質是每個非根節點所包含的關鍵字個數 j 滿足:┌m/2┐ - 1 <= j <= m;子樹的個數最多可以與關鍵字一樣多。非葉節點存儲的是子樹里最小的關鍵字。同時數據節點只存在於葉子節點中,且葉子節點間增加了橫向的指針,這樣順序遍歷所有數據將變得非常容易。

  4 B*樹:一棵m階B樹是一棵平衡的m路搜索樹。最重要的兩個性質是1每個非根節點所包含的關鍵字個數 j 滿足:┌m2/3┐ - 1 <= j <= m;2非葉節點間添加了橫向指針。

  B/B+/B*三種樹有相似的操作,比如檢索/插入/刪除節點。這里只重點關注插入節點的情況,且只分析他們在當前節點已滿情況下的插入操作,因為這個動作稍微復雜且能充分體現幾種樹的差異。與之對比的是檢索節點比較容易實現,而刪除節點只要完成與插入相反的過程即可(在實際應用中刪除並不是插入的完全逆操作,往往只刪除數據而保留下空間為后續使用)。

  先看B樹的分裂,下圖的紅色值即為每次新插入的節點。每當一個節點滿后,就需要發生分裂(分裂是一個遞歸過程,參考下面7的插入導致了兩層分裂),由於B樹的非葉子節點同樣保存了鍵值,所以已滿節點分裂后的值將分布在三個地方:1原節點,2原節點的父節點,3原節點的新建兄弟節點(參考5,7的插入過程)。分裂有可能導致樹的高度增加(參考3,7的插入過程),也可能不影響樹的高度(參考5,6的插入過程)。

  B+樹的分裂:當一個結點滿時,分配一個新的結點,並將原結點中1/2的數據復制到新結點,最后在父結點中增加新結點的指針;B+樹的分裂只影響原結點和父結點,而不會影響兄弟結點,所以它不需要指向兄弟節點的指針。

  B*樹的分裂:當一個結點滿時,如果它的下一個兄弟結點未滿,那么將一部分數據移到兄弟結點中,再在原結點插入關鍵字,最后修改父結點中兄弟結點的關鍵字(因為兄弟結點的關鍵字范圍改變了)。如果兄弟也滿了,則在原結點與兄弟結點之間增加新結點,並各復制1/3的數據到新結點,最后在父結點增加新結點的指針。可以看到B*樹的分裂非常巧妙,因為B*樹要保證分裂后的節點還要2/3滿,如果采用B+樹的方法,只是簡單的將已滿的節點一分為二,會導致每個節點只有1/2滿,這不滿足B*樹的要求了。所以B*樹采取的策略是在本節點滿后,繼續插入兄弟節點(這也是為什么B*樹需要在非葉子節點加一個兄弟間的鏈表),直到把兄弟節點也塞滿,然后拉上兄弟節點一起湊份子,自己和兄弟節點各出資1/3成立新節點,這樣的結果是3個節點剛好是2/3滿,達到B*樹的要求,皆大歡喜。

  B+樹適合作為數據庫的基礎結構,完全是因為計算機的內存-機械硬盤兩層存儲結構。內存可以完成快速的隨機訪問(隨機訪問即給出任意一個地址,要求返回這個地址存儲的數據)但是容量較小。而硬盤的隨機訪問要經過機械動作(1磁頭移動 2盤片轉動),訪問效率比內存低幾個數量級,但是硬盤容量較大。典型的數據庫容量大大超過可用內存大小,這就決定了在B+樹中檢索一條數據很可能要借助幾次磁盤IO操作來完成。如下圖所示:通常向下讀取一個節點的動作可能會是一次磁盤IO操作,不過非葉節點通常會在初始階段載入內存以加快訪問速度。同時為提高在節點間橫向遍歷速度,真實數據庫中可能會將圖中藍色的CPU計算/內存讀取優化成二叉搜索樹(InnoDB中的page directory機制)。

  真實數據庫中的B+樹應該是非常扁平的,可以通過向表中順序插入足夠數據的方式來驗證InnoDB中的B+樹到底有多扁平。我們通過如下圖的CREATE語句建立一個只有簡單字段的測試表,然后不斷添加數據來填充這個表。通過下圖的統計數據(來源見參考文獻1)可以分析出幾個直觀的結論,這幾個結論宏觀的展現了數據庫里B+樹的尺度。

  1 每個葉子節點存儲了468行數據,每個非葉子節點存儲了大約1200個鍵值,這是一棵平衡的1200路搜索樹!

  2 對於一個22.1G容量的表,也只需要高度為3的B+樹就能存儲了,這個容量大概能滿足很多應用的需要了。如果把高度增大到4,則B+樹的存儲容量立刻增大到25.9T之巨!

  3 對於一個22.1G容量的表,B+樹的高度是3,如果要把非葉節點全部加載到內存也只需要少於18.8M的內存(如何得出的這個結論?因為對於高度為2的樹,1203個葉子節點也只需要18.8M空間,而22.1G從良表的高度是3,非葉節點1204個。同時我們假設葉子節點的尺寸是大於非葉節點的,因為葉子節點存儲了行數據而非葉節點只有鍵和少量數據。),只使用如此少的內存就可以保證只需要一次磁盤IO操作就檢索出所需的數據,效率是非常之高的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM