B-tree
介紹
B-tree(平衡多路查詢樹)是自平衡樹的資料結構,維護已排序的資料。關於二叉樹和其它自平衡樹可檢視上篇紅黑樹。
一棵 \( m \) 階的樹滿足以下性質,
- 每個節點最多有\( m \)個子節點。
- 如果根不是葉節點,則根至少有兩個子節點。
- 每個非葉節點(根除外)至少有 \( {\frac{m}{2}} \) 個子節點。
- 具有 \( k \) 個子節點的非葉節點包含 \( k-1 \) 個鍵。
- 所有的葉子節點都具有相同的高度。
每個非內部節點的鍵充當分隔其子樹的分隔值。例如,下面是一棵 5 階樹的片段,內部節點有包含兩個鍵 [7, 16] ,所以它有三個子節點,最左邊子節點的鍵滿足小於7,中間子節點的鍵滿足大於7小於16,最右邊子節點的鍵滿足大於於16。
內部節點:內部節點是除葉節點和根節點之外的所有節點。
場景
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時,左邊節點到達最大鍵,需要拆分樹的節點。
插入新數字6 步驟如下:
- 先求出節點的中位數為 3;
- 建立一個新的葉節點,將中位數 3 之後的所有鍵複製到新節點;
- 將中位數3 上移,插入到父節點適當位置;
- 在中位數3 後新增一個父節點到新節點的指標;
- 將新數字 6 新增到新節點的正確位置。
合併樹的兩個節點
刪除鍵後,如果節點鍵數量小於最小鍵數時需要合併節點。下面樹一個節點可以擁有的最小鍵數為2。
刪除葉節點鍵1:
- 查詢到1刪除;
- 刪除3左指標,將 2 複製到 [4,5]節點,3下移;
- 3下移後,只有一個鍵6,父節點繼續下移,直到平衡。
刪除內部節點20:
- 查詢到 20 刪除,選取 20位置左子節點中最大值19 替換;
- 刪除 19位置左指標;
- 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]。
MySQL儲存引擎 InnoDB中的 B+tree
MySQL 建立表時會生成一個 .ibd檔案,這個ibd檔案是一個功能齊全的空間。每個空間又被拆分成多個頁面(Page),每一頁都會分配了一個 32 位整數頁號,每個頁面大小通常為16kB。
B+tree 索引中每個節點容量一個頁面的大小,葉子節點和非葉子節點資料型別不同。
葉子節點包含鍵值和下一條記錄的指標。
非葉子節點包含子頁面的頁碼和指向的子頁面的最小鍵。
同級節點之間,每個節點上一頁和下一頁的指標,形成同一級別頁面的雙向連結串列。
綜上索引結構如下,同級頁面形成雙向連結,在每個頁面內記錄升序連結,非葉頁面包含子頁面“指標”(子頁碼)。
關於B+tree 效率問題,可以檢視下錶
樹高度 | 非葉頁面 | 葉子頁面 | 行數 | 大小 |
---|---|---|---|---|
1 | 0 | 1 | 468 | 16.0 KB |
2 | 1 | 1203 | > 56.3 萬 | 18.8 MB |
3 | 1204 | 1447209 | > 6.77 億 | 22.1 GB |
4 | 1448413 | 1740992427 | > 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 級。