B-tree和B+tree 一種為資料查詢而生的結構

程式設計碼農發表於2021-11-26

B-tree

介紹

B-tree(平衡多路查詢樹)是自平衡樹的資料結構,維護已排序的資料。關於二叉樹和其它自平衡樹可檢視上篇紅黑樹

一棵 \( m \) 階的樹滿足以下性質,

  1. 每個節點最多有\( m \)個子節點。
  2. 如果根不是葉節點,則根至少有兩個子節點。
  3. 每個非葉節點(根除外)至少有 \( {\frac{m}{2}} \) 個子節點。
  4. 具有 \( k \) 個子節點的非葉節點包含 \( k-1 \) 個鍵。
  5. 所有的葉子節點都具有相同的高度。

每個非內部節點的鍵充當分隔其子樹的分隔值。例如,下面是一棵 5 階樹的片段,內部節點有包含兩個鍵 [7, 16] ,所以它有三個子節點,最左邊子節點的鍵滿足小於7,中間子節點的鍵滿足大於7小於16,最右邊子節點的鍵滿足大於於16。

內部節點:內部節點是除葉節點和根節點之外的所有節點。

B-tree

場景

B-tree 跟 紅黑樹應用場景不同,這種資料結構是為了處理大量資料而發明,它針對讀寫大資料量系統進行優化,常用於資料庫和檔案系統。

紅黑樹常用在應用程式,因為它處理的資料量一般不超過主存(RAM)容量。在資料庫場景中,資料量都是GB,TB級別,資料儲存在磁碟上,每次操作需要訪問磁碟讀取資料。

計算機儲存硬體中訪問資料的時間從快到慢依次如下:

  • 暫存器
  • CPU快取(L1、L2 和 L3)
  • 主記憶體(RAM)
  • 磁碟驅動器(固態磁碟)
  • 磁碟驅動器(磁碟)
執行典型指令                1/1000000000 秒 = 1納秒
從一級快取中讀取資料            0.5 納秒
從二級快取中讀取資料            7 納秒
從主記憶體中讀取資料                 100 納秒 
從新的磁碟位置讀取資料             8000000 納秒 = 8毫秒

從上面可以看出,主記憶體的訪問時間在納秒級別,磁碟訪問時間在毫秒級別。這意味著如果程式從磁碟讀取會比從主記憶體讀取慢 100, 000 倍。

所以 B-tree 優化目的就是減少磁碟訪問次數,通過下面方式:

  • 降低樹高度,使用多叉樹結構讓單個節點儲存更多個鍵
  • 資料以塊讀取,這樣一次可以讀取更多資料,一般來說節點容量等於磁碟塊大小。
1秒=1000毫秒=1000000微秒=1000000000納秒

自平衡

自平衡

拆分樹節點

下面是一個 6階B-tree(m=6),它一個節點可以擁有的最大鍵數是5,當插入數字6時,左邊節點到達最大鍵,需要拆分樹的節點。

btree-insert

插入新數字6 步驟如下:

  1. 先求出節點的中位數為 3;
  2. 建立一個新的葉節點,將中位數 3 之後的所有鍵複製到新節點;
  3. 將中位數3 上移,插入到父節點適當位置;
  4. 在中位數3 後新增一個父節點到新節點的指標;
  5. 將新數字 6 新增到新節點的正確位置。

合併樹的兩個節點

刪除鍵後,如果節點鍵數量小於最小鍵數時需要合併節點。下面樹一個節點可以擁有的最小鍵數為2。

btree-del

刪除葉節點鍵1:

  1. 查詢到1刪除;
  2. 刪除3左指標,將 2 複製到 [4,5]節點,3下移;
  3. 3下移後,只有一個鍵6,父節點繼續下移,直到平衡。

btree-del

刪除內部節點20:

  1. 查詢到 20 刪除,選取 20位置左子節點中最大值19 替換;
  2. 刪除 19位置左指標;
  3. 17 鍵下移到 [15,16]節點,18追加到後面。

B+tree

B+tree 是 B-tree 一個優化版本,用於資料庫索引。B+tree與B-tree的區別主要有兩個方面:

  • B+tree非葉子節點只儲存鍵,而B-tree所有節點都可以儲存鍵值;
  • B+tree 鍵對應的值都儲存在葉節點並且通過連結串列連結在一起。

下圖展示了B+tree儲存鍵值的情況,鍵 [1-7] 對應的值 [d1-d7]。

B+tree

MySQL儲存引擎 InnoDB中的 B+tree

MySQL 建立表時會生成一個 .ibd檔案,這個ibd檔案是一個功能齊全的空間。每個空間又被拆分成多個頁面(Page),每一頁都會分配了一個 32 位整數頁號,每個頁面大小通常為16kB。

B+tree 索引中每個節點容量一個頁面的大小,葉子節點非葉子節點資料型別不同。

葉子節點包含鍵值下一條記錄的指標

B_Tree_Simplified_Leaf_Page

非葉子節點包含子頁面的頁碼和指向的子頁面的最小鍵

B_Tree_Simplified_Non_Leaf_Page

同級節點之間,每個節點上一頁下一頁的指標,形成同一級別頁面的雙向連結串列。

B_Tree_Simplified_Level

綜上索引結構如下,同級頁面形成雙向連結,在每個頁面內記錄升序連結,非葉頁面包含子頁面“指標”(子頁碼)。

B_Tree_Structure

關於B+tree 效率問題,可以檢視下錶

樹高度非葉頁面葉子頁面行數大小
10146816.0 KB
211203> 56.3 萬18.8 MB
312041447209> 6.77 億22.1 GB
414484131740992427> 8140 億25.9 TB

大多數表索引高度再1-3級,所以一般只需要1-3次磁碟IO操作就可以完成。上面圖中描述的都是聚集索引(主鍵)。

資料庫中的 B+Tree索引分為聚集索引(clustered index)和次要索引(secondary index),聚集索引的葉子頁是包含整行資料的頁面,次要索引的葉子頁儲存了對應行的主鍵。

  • 使用聚集索引查詢可以直接獲得整行資料。
  • 使用次要索引查詢時,先查詢到主鍵值,然後再通過主鍵在聚集索引中找到完整的行資料。

小結

B-tree 是一種大資料量場景下的優化資料結構,旨在減少磁碟訪問次數(降低樹高度)。

B-tree 中的著名版本 B+tree 通過讓非葉節點只儲存鍵,佔用空間變小,使得每一層節點可以索引更多資料,有效控制了樹高度。

MYSQL的InnoDB中使用 B+tree 作為索引,正常情況下樹高度保持在 1-4 級。

相關文章