天天看點

MySQL資料庫要用B+樹存儲索引嗎,這個過程你應該沒有察覺

前言

什麼是索引 ​​一鍵擷取MySQL資料庫資料文檔​​

提起索引,大家都知道,建立索引可以讓資料庫查詢更快,那麼索引究竟是什麼?我想這就不是每個人都能說得出來了。 索引,是資料庫管理系統中一個排序的資料結構,并用以協助快速查詢、 更新資料庫表中資料。 是的,索引是一種資料結構,但是那麼多的資料結構中為何MySQL要選擇B+樹呢?接下來就讓我們一起來了解下B+樹相對于其他資料結構有何獨特之處!

二分查找法(Binary Search)

首先讓我們自己想一想,如果讓我們去設計,我們會怎麼去存儲?我想大部分人想到就是用連結清單或者數組去存儲資料,然後再按預設的順序排好,再去查找,而一個排好順序的連結清單我們就可以通過二分查找法來高效查詢。

二分查找也稱折半查找,是一種效率較高的查找方法。比如有1-10十個數,我們要找到8,先從中間開始找5,然後發現8比5大,可以把5左邊的數去掉,剩下6-10,再從中間開始找,依次類推,直到找到8為止。但是這種查找法有一個前提是資料必須是有序的,而且這種屬于連結清單式的存儲,我們一但要插入或者修改一個資料,可能會伴随着大量的下标移動,比如我們把1-10放在數組裡面,下标分别對應0-9,然後現在要插入一個0,為了保證有序,0必須排在第一位,那麼1-10所有的資料下标都要往後移動一位,這種就有點大動幹戈了,是以為了解決這個問題,我們就有了二叉樹。

二叉查找樹(BST)

二叉查找樹簡稱二叉樹(BST),英文全稱:Binary Search Tree,這是一種什麼樣的資料結構呢?請看下圖

在上面這棵樹中,我們要找到8,先從根節點6開始比較,發現8比6大,就往右邊走,就可以找到8

二叉樹的特點

二叉樹有兩個特點: 1、左子樹所有的節點都小于父節點 2、右子樹所有的節點都大于父節點

二叉樹存在的問題

二叉樹有一個嚴重的問題,那就是它的查找耗時是和這棵樹的深度相關的,在最壞的情況下時間複雜度會退化成 O(n)。 如下圖:

MySQL資料庫要用B+樹存儲索引嗎,這個過程你應該沒有察覺

上面就是一種極端情況下的二叉樹,會退化成線性連結清單,這種如果要找到最後一個數6,就要從1開始周遊完整棵樹,效率就會非常低。那麼有沒有一種相對平衡一點,不要出現這種極端情況的資料結構呢,是以就有了平衡二叉樹。

平衡二叉樹(AVL Tree)

平衡二叉樹,英文全名叫做 Balanced binary search trees,簡稱AVL樹,這個AVL并不是英文名的簡稱,而是發明者(G. M. Adelson-Velsky和E. M. Landis)兩個人的人名縮寫,請看下圖一個平衡二叉樹示例:

MySQL資料庫要用B+樹存儲索引嗎,這個過程你應該沒有察覺

上圖中也是從1開始插入6,如果是二叉樹就會變成一種線性結構,但是平衡二叉樹就會通過左旋和右旋操作,最終會生成上圖所示的結構

平衡二叉樹的特點

平衡二叉樹相比較二叉樹具有一個特點就是:左右子樹深度差絕對值不能超過 1,當然,平衡二叉樹首先是一顆二叉樹,隻不過通過左旋和右旋實作左右子樹深度差不超過1,避免了二叉樹的極端情況的出現。

MySQL為何不選擇平衡二叉樹

既然平衡二叉樹解決了普通二叉樹的問題,那麼mysql為何不選擇平衡二叉樹作為索引呢?

索引需要存儲什麼

讓我們想一想,如果我們要把索引存起來,那麼應該存哪些資訊呢,它應該存儲三塊資訊:

  • 索引的值:就是表裡面索引列對應的值。
  • 資料的磁盤位址(通過磁盤位址找到目前資料)或者直接存儲整條資料。
  • 子節點的引用:我們需要從根節點往下走,是以需要知道左右子節點的位址。 根據這三點,可以有如下大緻的一個簡單的結構圖:
MySQL資料庫要用B+樹存儲索引嗎,這個過程你應該沒有察覺

上圖中數字表示的是索引的值,0x開頭的表示磁盤位址,根節點中存了左右節點的引用。

AVL樹用來存儲索引存在什麼問題

我們知道,頁(Page)是 Innodb 存儲引擎用于管理資料的最小磁盤機關,頁的預設大小為16KB(InnoDB引擎的存儲結構後續我會專門寫一篇來講解,請關注我,和孤狼一起學習進步。)。頁也就是上圖中的節點,每查詢一次節點就需要進行一次IO操作,IO操作是一種非常耗時的操作,很多業務系統的瓶頸都是卡在IO操作上,是以如果我們需要提高查詢效率的辦法之一就是減少IO次數,那麼問題就來了,AVL樹一個節點上隻存了一個關鍵字(索引值)+一個磁盤位址+左右節點的引用,這是遠遠達不到16KB的,會浪費了大量的空間。

上圖中如果我們要找到6這條資料,需要進行3次IO(擷取一個節點就是一個IO操作),如果這棵樹很高的話,就會進行大量的IO操作,是以說AVL樹存在的最大問題就是空間利用不足,浪費了大量空間,資料量大的時候就會成為一顆瘦高的樹,那麼我們可以怎麼改進呢?答案很明顯了,那就是每個磁盤塊多存一點東西,也就是說每個磁盤多存幾個關鍵字,因為關鍵字越多,路數越多;路數越多,樹也就越矮越胖,相應的操作IO次數就會越少。

多路平衡樹(Balanced Tree)

多路平衡樹簡稱B樹,又稱B-樹,和AVL樹一樣,B樹在枝節點和葉子節點存儲鍵值、磁盤位址、左右節點引用。請看下圖的一個多路平衡樹的示例:

MySQL資料庫要用B+樹存儲索引嗎,這個過程你應該沒有察覺

B樹的特點

相比較AVL樹,B樹一個磁盤上可以存多個關鍵字(值),而且有一個特點就是:

  • 分叉數(路數)永遠比關鍵字數多1。 我們可以畫出如下簡圖(下圖中隻畫了3路,即兩個關鍵字,實際取決于一頁能存儲多少個關鍵字):
MySQL資料庫要用B+樹存儲索引嗎,這個過程你應該沒有察覺

從上圖可以很明顯的看出,同樣高度的樹,B樹能存的資料遠遠大于平衡二叉樹。

B樹是如何查找資料的

以上圖為例,假如我們要找key=32這個數字,首先擷取到根節點,發現18小于key,是以往右邊走,擷取到右邊的資料,54和76,這時候遵循以下原則:

  • key<54,命中最左邊分叉;
  • key=54,直接命中,傳回資料;
  • 54<key<76,走中間的一個分叉;
  • key=76,直接命中,傳回資料;
  • key>76,命中右邊分支; 這裡因為key=32,是以走得是第1條,命中左邊分支,這時候再去擷取左邊分支,擷取到32和50,比較發現key=32,命中,傳回資料。

從上面我們可以看出B樹效率相對于AVL樹,在資料量大的情況效率已經提高了很多,那麼為什麼MySQL還是不選擇B樹作為索引呢? 那麼接下來讓我們先看看改良版的B+樹,然後再下結論吧!

B+樹

B+樹由B樹改良而來,屬于改良版的多路平衡查找樹。 首先讓我們來看看B+樹到底長什麼樣呢:

MySQL資料庫要用B+樹存儲索引嗎,這個過程你應該沒有察覺

對比B+樹,我們可以發現一個很明顯的差別就是葉子節點有一個箭頭指引而且從左到右是有序的。

InnoDB中使用的B+樹相比較于傳統B+樹,改進之後的B+樹具有以下特點

InnoDB中B+樹的特點

  • 它的關鍵字的數量是跟路數相等的。
  • B+樹的根節點和枝節點中都不會存儲資料,隻有葉子節點才存儲資料。而搜尋到關鍵字不會直接傳回,會到最後一層的葉子節點。
  • B+樹的每個葉子節點增加了一個指向相鄰葉子節點的指針,它的最後一個資料會指向下一個葉子節點的第一個資料,形成了一個有序連結清單的結構。
  • 它是根據左閉右開的區間來檢索資料的 按照B+樹的特點,我們可以畫出一個存儲資料的簡圖,如下:
MySQL資料庫要用B+樹存儲索引嗎,這個過程你應該沒有察覺

B+樹是如何查找資料的

假設我們現在要找一個key=66,遵循如下步驟: 1、擷取到根節點,依據左閉右開有如下區間:[1,28),[28,66),[66,+∞),命中了最後一個區間,雖然66在根節點,但是因為根節點不存儲資料,是以是會往下繼續搜尋右邊的節點 2、擷取到右邊節點,依據左閉右開有如下區間:[66,78),[78,89),[89,+∞),命中左邊的範圍。 3、擷取到第三排倒數第二塊磁盤,找到66,傳回資料。

B+樹相對于B樹的改進點

B+樹是由B樹改進而來的,是以B樹能解決的問題,B+樹都能解決,那麼B+樹能解決哪些B樹所不能解決的問題呢? 1、掃庫、掃表能力更強:如果我們要對表進行全表掃描,隻需要周遊葉子節點就可以 了,不需要周遊整棵B+Tree 2、B+Tree 的磁盤讀寫能力相對于 B Tree 來說更強:根節點和枝節點不儲存資料區, 是以一個節點可以儲存更多的關鍵字,一次磁盤加載(IO操作)能擷取到相對更多的關鍵字。 3、天然具備排序能力:葉子節點上有下一個資料區的指針,資料形成了連結清單。 4、效率穩定:B+Tree 永遠是在葉子節點拿到資料,是以 IO 次數是穩定的,而B樹運氣好根節點就拿到資料,運氣不好就要到葉子節點才能拿到資料,所花費的時間會有差異。

總結

本文簡述了從二叉樹到B+樹之前的演進過程,并大緻講解了各種資料結構之間的差異以及MySQL為何最終會選擇了B+樹來作為索引。

最後

MySQL資料庫要用B+樹存儲索引嗎,這個過程你應該沒有察覺