cmu15545-資料訪問方式:B+樹(B+Tree)

咪啪魔女發表於2024-11-13

目錄
  • 基本概念
    • 基於磁碟的B+樹
    • 查詢與索引
  • 設計選擇
    • 結點大小(Node Size)
    • 合併閾值(Merge Thredshold)
    • 變長鍵(Variable-length Keys)
    • 結點內部搜尋(Intra-Node Search)
  • 最佳化手段
    • Pointer Swizzling
    • Bε-trees
    • Bulk Insert
    • Prefix Compression
    • Deduplication
    • Suffix Truncation

基本概念

基於磁碟的B+樹

為什麼使用B+數進行資料訪問(Access Method):

image-20241109143725399

  • 天然有序,支援範圍查詢
  • 支援遍歷所有資料,利用順序IO
  • 時間複雜度為\(O(logn)\),滿足效能需求
  • 相比於B樹,資料訪問都在葉子結點:磁碟空間利用率高;併發衝突減少

一個基礎的B+樹:

  • 三類借點:根結點,中間結點,葉子結點
  • 資料分佈:根結點和中間結點只儲存索引,葉子結點儲存資料
  • 指標關係:父子指標,兄弟指標

image-20241113104944036

基於磁碟的B+樹映象:

一個結點儲存在一個堆檔案(Heap File)頁中;頁ID(PageId)代替指標的作用。

  • 鍵值聯合儲存

    image-20241113105746186

  • 鍵值分別儲存

    image-20241113105801713

B+樹的葉子結點儲存實際資料,這個資料如何理解,取決於不同的資料庫實現:有些儲存RecordID,有些基於索引組織(Index-Organized Storage)的資料庫則直接儲存元組(Tuple)資料。

image-20241113110032259

如果不瞭解RecordID,資料組織方式,可以參看這篇博文

查詢與索引

最左字首匹配

有聯合索引<a,b,c>,支援如下查詢條件

  • (a=1 AND b=2 AND c=3)
  • (a=1 AND b=2)

image-20241113111703944

如果所有不滿足最左字首匹配原則,需要全表掃描。

如何處理重複鍵

  • 加上RecordID使其變成唯一鍵

    image-20241113111945385

  • 葉子結點溢位(沒有實際系統採用)

    image-20241113111956652

聚簇索引

  • 一個表只能有一個聚簇索引
  • 索引鍵和值儲存在一起
  • 資料按照索引的鍵排序
  • 運算元據時要同步操作索引

聚簇索引是非必須的,取決於資料庫具體實現,Mysql和SQLite中資料直接用聚簇索引組織。

用B+樹實現聚簇索引可以很方便地實現範圍查詢和便利,充分利用順序IO。

image-20241113112518741

對於非聚簇索引,雖然索引的鍵有序,但是對應的資料在磁碟上不一定是順序儲存的,所以很有效的方式是先得到PageID,後根據PageID進行排序,最後獲取資料,充分利用順序IO。
image-20241113112630957

設計選擇

結點大小(Node Size)

儲存裝置讀取資料越慢,越需要利用順序IO,結點就越大;

儲存裝置讀取資料越快,越需要減少冗餘資料讀取,結點就越小。

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

合併閾值(Merge Thredshold)

結點中的鍵數量低於半滿的時候,不會立刻進行合併,而是允許小結點存在,然後再週期性地重建整棵樹。

PostgreSQL中稱其為不平衡的B+樹("non-balanced" B+Tree, nbtree)。

變長鍵(Variable-length Keys)

  • 指標:鍵儲存指向實際資料的指標【無法利用順序IO,因為要跳轉去讀取指標內容】
  • 變長結點
  • 填充資料(Padding)

實際系統中的索引資料和堆檔案資料一樣,是能存結點就存結點中,是在存不下存指標。

  • 線性查詢:由於SIMD指令集存在,實際順序查詢,其實可以是批處理

    image
  • 二分查詢

  • 插值法:鍵沒有間隙的時候(自增),可以直接計算出偏移

    image

最佳化手段

Pointer Swizzling

基本思想:當一個物件從磁碟載入到記憶體時,將其磁碟地址轉換成記憶體地址(swizzling),以便程式在記憶體中直接透過指標訪問。

例子:比如主鍵索引的B+樹根結點讀取到Buffer Pool後,會被pin住,不被置換出去,所以此時可以直接用記憶體指標訪問根結點,省略用PageID問Buffer Pool要記憶體地址的步驟。

圖示:

image image

Bε-trees

一種B+樹的寫最佳化。

基本思想:更新時不直接修改資料 ,而是記錄日誌(類似於log-structured data storage)。

日誌記錄在結點上,當結點日誌記錄滿以後,該結點的日誌下推到孩子結點。

image-20241113133509231

image-20241113133530151

Bulk Insert

基本思想:由底至頂建立B+樹,而不是由頂至底。

減少了插入時樹的結構變化,前提是需要預先排序資料。

Keys: 3, 7, 9, 13, 6, 1
Sorted Keys: 1, 3, 6, 7, 9, 13

image-20241113133953890

image-20241113134001752

Prefix Compression

基本思想:字典序壓縮字首。

cmu15545-資料訪問方式:B+樹(B+Tree)

Deduplication

基本思想:非唯一索引中避免重複儲存相同鍵。

cmu15545-資料訪問方式:B+樹(B+Tree)

Suffix Truncation

基本思想:中間結點只是起引路作用,所以儲存能辨識的最小字首即可。

image image

相關文章