rknfish

rknfish

i need more love
github

講座#08:樹索引

參考課程

參考筆記

參考投影片

封面

[[目錄]]


1. 表索引#

表索引是表屬性的副本,主要用於加速。

表索引主要權衡兩個方面的因素

  • 維護的代價
  • 加速的效果

2. B+ 樹#

B + 樹屬性

B+ 樹是一種自平衡的數據結構,查找插入等操作的時間複雜度通常在 $O (\log {n})$ ,並且這種數據結構針對比較大的數據塊做了優化。

B+ 樹是一個 M- 叉搜索樹有以下的特徵:

  • 完美平衡(所有的葉子節點的層數都相同)
  • 除了根節點外,左右節點的有 M/2-1 <= #keys <= M-1
  • 所有有 k 個 key 的節點都有 k+1 個非空子節點。

B + 樹示例

在 B+ 樹中,葉子節點是存儲最後我們需要的 key 的地方。因此我們如果要依據葉子節點去尋找最終的 val 的話,有以下兩種方法。

  1. 將 val 存在的地方用一個指針記錄下地址
  2. 直接存儲在葉子節點中。

B-Tree VS. B+Tree#

B - 樹和 B + 樹最主要區別是,B - 樹的 key 也存在於非葉子節點。因此 B - 樹在利用存儲空間上無疑相較於 B + 樹更為優秀。

因此可能存在以下的一些問題。

  1. B - 樹在遍歷的時候不得不在各個節點中來回跳躍。而 B + 樹則可以直接掃描。
  2. B - 樹,在使用將 val 存儲在節點中的方法可能會導致查詢過慢。

可視化#

https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html

插入#

找到一個葉子節點進行插入。按照大小插入。

如果該節點有足夠的空間容納,那麼則插入停止。如果沒有足夠的空間那麼有以下兩步操作。

  1. 將該節點以中間值對半分為兩個節點。
  2. 將該中間值向上推給父節點。若沒有父節點,則創造父節點。然後對父節點執行同樣的操作。

刪除#

找到葉子節點進行刪除。

如果該葉子節點刪除了該 key 之後至少有一半的容量,那麼刪除停止。

如果該葉子節點只有 M/2-1 個 key 了,那麼有以下兩種策略

  • 嘗試將隔壁節點的值拿過來。
  • 如果嘗試失敗那麼則嘗試將兩個節點合併。

查詢條件#

具有與樹結構查詢的優缺點。

重複的 keys#

有兩種方式來解決重複的 keys

  1. 在所有的 key 後面加入 recorid ID 來確保每個 key 都是獨一無二的。
  2. 使用一個可以溢出的節點來保存這些重複的信息。
    溢出節點

聚集索引#

表通常依照 primary key 進行索引。

不過有一些數據庫採用聚集索引,會為表加上一個隱藏的 primary key。別的數據庫通常無法使用這個索引


3. B+ 樹設計選擇#

節點大小#

通常是存儲設備越慢,採用的節點大小越大。

  • HDD: ~1MB
  • SSD: ~10KB
  • In-Memory: ~512B

合併閾值#

一些 DBMS 不會總是合併那些 half full 狀態的節點。

因為,如果延遲合併操作的話,那麼可能會減少重新平衡的開銷。

維持一些比較小的節點並且週期性的重構樹可能會更好。

可變長 keys#

  1. 採用指針的方法,即在 B + 樹中僅僅存儲指向值的指針。
  2. 採用可變長的節點。
  3. 將所有的值強制以一個固定大小存儲在 B+ 樹中。
  4. 採用 map 的形式來進行存儲

節點內搜索#

  1. 線性
    • 線性的掃描節點
    • 可以採用 SIMD 的技術增加掃描的數據量。
  2. 二分
    採用二分法進行查找
  3. 插值
    通過值,從而知道位置在哪。
    插值

4. 優化#

前綴壓縮#

通過提取出相同前綴的方式來進行壓縮。

前綴壓縮

重複消除#

在 index 不一定都是獨一無二的所有的值中,可以通過將這些值合併為一個 key 。

重複消除

後綴截斷#

這種方式是通過在節點中選出可以進行區分比較的小的前綴長度,從而達到查找的目的。

截斷 1

截斷 2

指針替換#

將 B+ 樹中的 page id 固定在 buffer pool 中,可以避免重複的讀 disk 進而加快速度。然後將 page id 的指針替換為在 memory 中的真實的值,這樣就可以繞過 buffer pool 的轉換,進行直接訪問。

批量插入#

重建一個 b+ 樹的方法最快的方法是從底層向上建樹

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。