【連載】關係型數據庫是如何工作的?(5) - B+Tree索引

雖然上一章節介紹的二叉搜索樹在查詢指定值時表現很好,但是當查詢兩個值之間的多個節點時,就會遇到很大的問題。因爲需要遍歷整個樹的節點,並檢查每個節點是否在指定的區間內。而且遍歷整顆樹是隨機磁盤IO(譯者注:隨機IO會導致頻繁的磁頭換道,所以相比順序IO來說非常耗時),所以我們需要找到一種更有效做範圍查詢的方法。爲了解決這個難題,現代數據庫修正了之前介紹的二叉搜索樹,我們稱修正後的數據結構爲B+Tree:

  • 只有葉子節點(樹最底層的節點,圖中橘黃色的節點)存儲信息,即:行在表中精確的位置,也就是rowid;
  • 其他的節點僅僅是在搜索時用於路由到正確的節點。

B+Tree

就像上圖中所示,有了更多的節點,實際上這些額外的節點就是決策節點,它會幫助我們找到正確的節點(實際存儲行精確位置),但是它的搜索複雜度卻依然是O(log(N)),和搜索二叉樹最大的不同就是葉子節點都持有下一個節點的指針(譯者注:可以看做一個有序的單向鏈表)。

使用B+Tree,如果我們需要查詢40到100之間的值:

  • 你僅僅需要找到40節點,或者如果40節點不存在則找到大於40的最近節點;
  • 根據上一步找到的節點持有的指針,樹藤摸瓜找到下一個節點,直到找到100節點截止。

譯者注:上圖其實也間接的解釋了數據庫是怎麼構建B+Tree索引的,應該是自底向上的來構建。

假設實際需要在一棵有N個節點的樹中範圍查找到M個節點,那麼其時間複雜度爲M+log(N)。因爲找到開始節點需要log(N),接着就可以根據指針按順序找到M個節點,實際耗費M。M+log(N)與之前二叉搜索書的N相比,你不需要搜索整顆樹,僅僅搜索M+log(N)個節點,這意味着更少的磁盤IO消耗;如果M很小(比如:200行),而N很大(比如:1 000 000行),那這兩者的差距就非常巨大了。

可是我們又發現了一個問題,如果數據庫使用B+Tree索引,而你刪除一張表中的某一行,那麼:

  • 你必須保持整棵樹中節點的順序,否則在亂序的樹種你無法找到正確的節點。
  • 你必須保證樹的高度儘可能低,否則無論單值查詢還是範圍查詢的複雜度就會從O(log(N))無限的接近O(N)。(譯者注:當樹的高度就是節點的數量時,那麼樹的外形就像一條豎線,這時要找到一個節點實際上需要遍歷整顆樹

簡而言之,B+Tree需要自排序和自平衡。雖然我們可以高效快速的刪除和插入,但這是有代價的:在B+Tree數據庫中代價是O(log(N))。這就是爲什麼你經常看到創建過多的索引不是一個好主意,這樣會降低在表中插入/刪除/更新行的速度。究其原因,是每一次插入/刪除/更新,都會導致數據庫更新(譯者注:指自排序和自平衡)表的索引樹,並且這種更新每個索引耗費是O(log(N))(譯者注:上文指的代價)。而且,增加索引會導致事務管理器的負載增大(後續篇章會介紹到)。

關於B+Tree的更多細節,可以查看維基百科中的B+Tree說明。如果你想找B+Tree在數據庫中的實現例子,可以查看來自MySQL核心開發者貢獻的這兩篇關於InnoDB如何處理索引的文章:The physical structure of InnoDB index pagesB+Tree index structures in InnoDB

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章