B+ 樹的概念
基本概念
B+ 樹是 B 樹的一種變體,從某個程度上看,B+ 樹可以認定是 B 樹的升級版。
在 B+ 樹中,關鍵字只儲存在葉子結點,非葉子結點儲存的是葉子結點所儲存關鍵字的部分拷貝,所有的葉子結點也都在相同的高度,葉子結點本身按關鍵字大小從小到大連結。
因此,相對於 B 樹而言,B+ 樹更充分地利用了結點的空間,讓查詢速度更加穩定,其速度完全接近於二分查詢。
特性
B+ 樹擁有 B 樹的大部分特性,但也具有獨特的、與 B 樹不同的特性,不同的地方有以下兩點:
- B+ 樹的非葉子結點不直接儲存資料的指標,所有資料的指標都儲存在葉子結點
- B+ 樹葉子結點儲存的資料從小到大有序排列,且相鄰葉子結點之間具有連結
與 B 樹的區別
與 B 樹相比較,B+ 樹具有以下特點:
- B+ 樹的非葉子結點不直接儲存資料,儲存的索引更多,樹的層級更少,查詢的速度更快
- B+ 樹所有資料的指標都儲存在葉子結點,因此每次查詢到資料的次數都相同,查詢速度更穩定
- B+ 樹所有的葉子結點之間具有連結,構成了一個有序連結串列,查詢範圍區間的資料更方便
- B+ 樹遍歷所有資料時只需要遍歷所有葉子結點即可,相對 B 樹遍歷更快
為什麼使用 B+ 樹作為索引結構?
索引的本質是一種用於快速查詢記錄的資料結構,常見有二叉查詢樹、平衡二叉樹、雜湊表、B 樹和 B+ 樹等索引儲存結構。
每一種索引結構都有其對應的應用場景,易用性也是選擇的標準之一,這裡討論一下為什麼選用 B+ 樹作為索引儲存結構。
為什麼不採用二叉查詢樹?
使用普通的二叉樹查詢作為索引結構具有一個致命的問題:當一直插入資料的時候,有可能會退化成連結串列結構,時間複雜度也會從 \(O(\log n)\) 退化到 \(O(n)\)。
因此,普通的二叉查詢樹比較適合資料基本沒有變動的情況,這樣查詢效率不會發生較大的變化。
為什麼不採用平衡二叉樹?
為了解決普通二叉查詢樹有可能退化成連結串列的問題,可以使用自平衡的二叉查詢樹代替,如 AVL 樹、紅黑樹等。
紅黑樹常見的一種自平衡二叉查詢樹,但是也有一個問題:紅黑樹是一個近似平衡的二叉樹,當資料量較大的時候,會出現樹層級較大的情況。
當資料量非常大時,索引佔用的空間也會非常大,索引還是得儲存在磁碟上,如果樹的層級較大,則進行磁碟 IO 的次數就會越多,效能就會越差。
因此,紅黑樹不適合作為儲存在磁碟上的索引結構。
為什麼不採用雜湊表?
雜湊表是一個支援快速查詢的資料結構,其查詢的時間複雜度是 \(O(1)\),其也是最常見的索引儲存結構之一。
但是雜湊表也有其缺點,就是隻儲存鍵值對應關係的雜湊表不支援範圍查詢,如果要做範圍查詢,需要做全量的資料掃描才行。
當然,如果是具有排序功能的雜湊表,會非常適合作為儲存在記憶體中的索引結果,如 Java 中的 TreeMap 物件。
為什麼不採用 B 樹?
使用 B 樹可以解決紅黑樹層級較大的問題,通過一個結點可以儲存多個元素,樹變得更加矮胖,使得樹的層級變得可控。
而且,通過給一個結點儲存一頁的資料量,最大化地優化作業系統和磁碟的互動,解決了多次磁碟 IO 的問題。
但是對應 B+ 樹而言,B 樹的層級仍然會比 B+ 樹的高,且範圍查詢沒有 B+ 樹方便,這是捨棄 B 樹而選擇 B+ 樹的主要原因。
當然,也有使用 B 樹作為索引結構的資料庫,如 MongoDB 等。