為什么 Mysql 用 B + 樹做索引而不用 B 樹或紅黑樹
B + 樹只有葉節點存放數據,其余節點用來索引,而 B -樹是每個索引節點都會有 Data 域。所以從 Mysql(Inoodb)的角度來看,B + 樹是用來充當索引的,一般來說索引非常大,尤其是關系性數據庫這種數據量大的索引能達到億級別,所以為了減少內存的占用,索引也會被存儲在磁盤上。
那么 Mysql 如何衡量查詢效率呢?– 磁盤 IO 次數。 B - 樹 / B + 樹 的特點就是每層節點數目非常多,層數很少,目的就是為了就少磁盤 IO 次數,但是 B - 樹的每個節點都有 data 域(指針),這無疑增大了節點大小,說白了增加了磁盤 IO 次數(磁盤 IO 一次讀出的數據量大小是固定的,單個數據變大,每次讀出的就少,IO 次數增多,一次 IO 多耗時),而 B + 樹除了葉子節點其它節點並不存儲數據,節點小,磁盤 IO 次數就少。這是優點之一。
另一個優點是: B + 樹所有的 Data 域在葉子節點,一般來說都會進行一個優化,就是將所有的葉子節點用指針串起來。這樣遍歷葉子節點就能獲得全部數據,這樣就能進行區間訪問啦。在數據庫中基於范圍的查詢是非常頻繁的,而 B 樹不支持這樣的遍歷操作。
B 樹相對於紅黑樹的區別
AVL 樹和紅黑樹基本都是存儲在內存中才會使用的數據結構。在大規模數據存儲的時候,紅黑樹往往出現由於樹的深度過大而造成磁盤 IO 讀寫過於頻繁,進而導致效率低下的情況。為什么會出現這樣的情況,我們知道要獲取磁盤上數據,必須先通過磁盤移動臂移動到數據所在的柱面,然后找到指定盤面,接着旋轉盤面找到數據所在的磁道,最后對數據進行讀寫。磁盤 IO 代價主要花費在查找所需的柱面上,樹的深度過大會造成磁盤 IO 頻繁讀寫。根據磁盤查找存取的次數往往由樹的高度所決定,所以,只要我們通過某種較好的樹結構減少樹的結構盡量減少樹的高度,B 樹可以有多個子女,從幾十到上千,可以降低樹的高度。
我們先來看二叉樹查找時磁盤IO的次:定義一個樹高為4的二叉樹,查找值為10:
第一次磁盤IO:
第二次磁盤IO
第三次磁盤IO:
第四次磁盤IO:
而對於B樹:
如下有一個3階的B樹,觀察查找元素21的過程:
第一次磁盤IO:
第二次磁盤IO:
這里有一次內存比對:分別跟3與12比對
第三次磁盤IO:
這里有一次內存比對,分別跟14與21比對
從查找過程中發現,B樹的比對次數和磁盤IO的次數與二叉樹相差不了多少,所以這樣看來並沒有什么優勢。
但是仔細一看會發現,比對是在內存中完成中,不涉及到磁盤IO,耗時可以忽略不計。另外B樹中一個節點中可以存放很多的key(個數由樹階決定)。
相同數量的key在B樹中生成的節點要遠遠少於二叉樹中的節點,相差的節點數量就等同於磁盤IO的次數。這樣到達一定數量后,性能的差異就顯現出來了。
數據庫系統的設計者巧妙利用了磁盤預讀原理,將一個節點的大小設為等於一個頁,這樣每個節點只需要一次 I/O 就可以完全載入。為了達到這個目的,在實際實現 B-Tree 還需要使用如下技巧:每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁里,加之計算機存儲分配都是按頁對齊的,就實現了一個 node 只需一次 I/O。
好的,我總結一下:
- 使用B+樹而不是B數的原因
- B+樹節點大小更小,一次IO讀入的節點數更多
- B+樹的數據都在葉子節點中,遍歷和區間訪問性能大幅提高
- B+樹查詢效率穩定
- 使用B+樹而不是AVL樹、紅黑樹的原因
- B+樹的樹高比AVL樹、紅黑樹低,IO次數少