B樹和B+樹簡單分析總結

本文參考 公衆號 程序員小灰中關於B+樹的講解

​ MySQL索引主要基於Hash和B+樹的數據結構,本文將對B樹和B+樹進行一個簡單的描述。

​ 我們都知道,樹的查詢效率高,並且可以保持有序,但MySQL並未使用二叉查找樹這種更快的查找方式來設計索引,是由於在服務器上查找時必須考慮到IO因素,並且數據庫索引是保存在磁盤上的,當數據量比較大時,索引大小甚至可以到到G單位以上,當我們利用索引進行查詢時,不可能把整個索引全部加載到內存上,只能逐個加載每一個磁盤頁,也就是索引樹的節點。下面將通過比較二叉樹和B+樹分別作爲索引結構的查詢過程。

一、二叉樹作爲索引結構

​ 當使用二叉樹作爲索引結構時,假設樹的高度爲4,查找值爲10,流程會如下:

1、二叉查找樹的結構:

2、磁盤IO訪問情況

可以看出,磁盤IO的次數是4次,索引樹的高度也爲4,最壞情況下,磁盤IO次數等於索引的高度,所以爲了減少磁盤IO次數,需要壓縮減少樹的高度,這也就成爲了B樹的特徵之一。

二、B樹

1、結構概述

​ B樹是一個多路平衡查找樹,每一個節點最多包含k個孩子,k也被稱爲B樹的階。k的大小取決於磁盤頁的大小。一個m階的B樹有下面幾個特徵:

  • 根節點至少有兩個子節點
  • 每個中間節點都包含k-1個元素和k個孩子,其中m/2 ≤ k ≤ m
  • 每個葉子節點都包含k-1個元素,其中m/2 ≤ k ≤ m
  • 所有葉子節點都位於同一層
  • 每個節點的元素都從小到大排列,節點中k-1個元素正好是k個孩子包含的元素的值域劃分。

其中第二第三條表示:即B樹葉子節點元素數量最多爲階樹 - 1.例如一個4階的B樹,每個節點最多包含有4個孩子,3個元素,最少包含2個孩子,1個元素

​ 下面以一個3階的B樹爲例,來描述B樹的具體結構。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-XLESHucR-1588609318309)(%E4%BA%8C%E5%8F%89%E6%9F%A5%E6%89%BE%E6%A0%91/7.png)]

​ 這裏以(2, 6)節點爲例,該節點有兩個元素2和6,有三個孩子1,(3, 5),8;其中1小於2,3和5大於2且小於6,8大於3和5,符合上面所列的幾個特徵。

2、查詢過程

​ 假設要查詢的數值是5,磁盤IO訪問情況如下:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Sb4wiBiR-1588609318342)(%E4%BA%8C%E5%8F%89%E6%9F%A5%E6%89%BE%E6%A0%91/8.png)]

可以看出,B樹在查詢過程中比較次數其實並不會比二叉查找樹少,尤其當某一個節點中的元素數量很多的時候,可是,單個節點的元素比較都在內存中進行,比通過磁盤IO來進行比較,性能提高比較大,所以只要樹的高度足夠低,IO數量足夠少,查詢性能提升越明顯。 相比之下,只要不超過磁盤頁的大小,僅僅是多了幾次內存的交互,這也是B樹的優勢之一。

3、插入過程

​ 以上面的B樹爲例,若想要插入4,會進行下面的操作:

  1. 自頂而下查找4的節點位置,發現4應該插入到節點元素3,5之間

  1. 該B樹爲3階,根據上面B樹的特徵,節點3,5已經是兩元素節點,無法再新增元素。父親節點2,6也是兩元素節點,同樣無法再增加。根節點9是個單元素節點,可以升級到兩元素節點。於是拆分節點3,5與節點2,6,讓根節點升級爲兩元素節點4,9.節點6獨立爲根節點的第二個孩子。

    ​ 可以看到,爲了插入一個元素,整個B樹的很多節點都發生了改變,但也正因爲此,B樹始終可以維持多路平衡,自平衡也是B樹的一大優勢。

4、刪除過程

​ 以上面插入後的B樹爲例,若想刪除元素11,會進行以下的操作:

​ 可以看出,刪除元素11後,節點12只有一個孩子節點,不符合B樹規範。因此找到12,13,15的中位數13,取代節點12,而節點12自身下移成爲第一個孩子節點(這個過程也叫做左旋)。具體過程如下圖:

三、B+樹

​ B樹主要應用於文件系統以及部分數據庫索引,非關係數據庫MongoDB使用的就是B樹索引,而大部分使用的關係型數據庫(MySQL)使用的都是B+樹索引。

1、結構概述

​ B+樹是基於B樹的一種變形,有着比B樹更高的查詢性能。

​ 一個m階的B+樹具有下面幾個特徵:

  • 有k個子樹的中間節點包含有k個元素(B樹中是k-1個元素),每個元素不保存數據,只用來索引,所有數據都保存在葉子節點中
  • 所有的葉子節點中包含了全部元素的信息和指向含這些元素記錄的指針,並且葉子節點本身依照關鍵字的大小自小到大順序連接
  • 所有的中間節點元素都同時存在於子節點,並且在子節點中是最大或最小的元素

​ 下面用一個示例來描述B+樹的特徵:

​ 可以看出,每個父節點的元素都出現在子節點中,並且是子節點的最大或最小的元素。在上面的示例中,根節點元素8是子節點(2, 5, 8)的最大元素,也是葉子節點(6, 8)的最大元素;根節點15是子節點(11, 15)的最大元素,也是葉子節點(13, 15)的最大元素。**並且根節點的最大元素(這裏是15)也就是整個B+樹的最大元素。之後無論插入或刪除多少元素,始終要保證最大元素在根節點中。**葉子節點包含了全部的元素信息,並且每個葉子節點都擁有指向下一個節點的指針,形成了一個有序鏈表。

​ B+樹另外一個和B樹不同的地方就是衛星數據(指的是索引元素所指向的數據記錄,比如數據庫中的某一行)的位置:B樹中所有節點都帶有衛星數據,而在B+樹中,只有葉子節點帶有衛星數據,其餘中間節點僅僅是索引,沒有任何的數據關聯。

​ 在數據庫的設計中,聚集索引(Clustered Index)中,葉子節點直接包含了衛星數據。在非聚集索引(NonClustered Index)中,葉子節點帶有指向衛星數據的指針,具體的在數據庫索引設計的文章中進行描述。

2、單元素查詢過程

​ 單元素查詢過程:在單元素查詢時,B+樹會自頂向下逐層查找節點,最終找到匹配的葉子節點,例如查詢元素3:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1OXeo9AH-1588609318345)(%E4%BA%8C%E5%8F%89%E6%9F%A5%E6%89%BE%E6%A0%91/13.png)]

​ 和B樹的查詢有兩點不同:B+樹的中間節點沒有衛星數據,所以同樣大小的磁盤頁可以容納更多的節點元素,也意味着數據量相同的情況下,B+樹的結構比B樹更加“矮胖”,因此查詢IO次數更少;B+樹的查詢必須最終找到葉子節點,而B樹只要匹配到元素即可,不論它是中間節點還是葉子節點,因此B樹的查詢性能並不穩定(最好情況是隻查詢根節點,最壞情況查詢到葉子節點),而B+樹每一次查找都是穩定的。

3、範圍查詢過程

​ B樹只能依靠中序遍歷來實現範圍查詢,例如我們查詢3到11之間的元素:

B樹範圍查找過程:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-8p7XQdEm-1588609318364)(%E4%BA%8C%E5%8F%89%E6%9F%A5%E6%89%BE%E6%A0%91/14.png)]


B+樹範圍查找過程:

​ B+樹之遙在鏈表上做遍歷即可

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-pUCFj0Fx-1588609318368)(%E4%BA%8C%E5%8F%89%E6%9F%A5%E6%89%BE%E6%A0%91/15.png)]

4、插入和刪除

​ B+樹的插入和刪除操作和B樹類似

5、B+樹優勢

  • IO次數更少:單一節點比B樹存儲了更多的數據,使得查詢IO的次數減少
  • 查詢性能穩定:所有查詢都要查找到葉子節點,查詢性能穩定
  • 範圍查詢簡便:所有葉子節點形成有序鏈表,便於範圍查詢
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章