1. 程式人生 > 資料庫 >【PHP】你知道MySQL索引為什麼要選擇B+樹呢

【PHP】你知道MySQL索引為什麼要選擇B+樹呢

宣告

本文所述的各種資料結構(二叉樹等),均不考慮重複值的情況,本文簡述各種資料結構的區別僅僅只是為了理解MySQL索引的需要而做的鋪墊。

什麼是索引

提起索引,大家都知道,建立索引可以讓資料庫查詢更快,那麼索引究竟是什麼?我想這就不是每個人都能說得出來了。

索引,是資料庫管理系統中一個排序的資料結構,並用以協助快速查詢、 更新資料庫表中資料。

是的,索引是一種資料結構,但是那麼多的資料結構中為何MySQL要選擇B+樹呢?接下來就讓我們一起來了解下B+樹相對於其他資料結構有何獨特之處!

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

二分查詢也稱折半查詢,是一種效率較高的查詢方法。比如有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)。

如下圖:

在這裡插入圖片描述

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

平衡二叉樹(AVL Tree)

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

在這裡插入圖片描述

上圖中也是從1開始插入6,如果是二叉樹就會變成一種線性結構,但是平衡二叉樹就會通過左旋和右旋操作,最終會生成上圖所示的結構,感興趣的可以進入網站自己操作觀察旋轉過程.

平衡二叉樹的特點

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

MySQL為何不選擇平衡二叉樹

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

索引需要儲存什麼

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

  • 索引的值:就是表裡面索引列對應的值。

  • 資料的磁碟地址(通過磁碟地址找到當前資料)或者直接儲存整條資料。

  • 子節點的引用:我們需要從根節點往下走,所以需要知道左右子節點的地址。

根據這三點,可以有如下大致的一個簡單的結構圖:

在這裡插入圖片描述

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

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

我們知道,頁(Page)是 Innodb 儲存引擎用於管理資料的最小磁碟單位,頁的預設大小為16KB。頁也就是上圖中的節點,每查詢一次節點就需要進行一次IO操作,IO操作是一種非常耗時的操作,很多業務系統的瓶頸都是卡在IO操作上,所以如果我們需要提高查詢效率的辦法之一就是減少IO次數,那麼問題就來了,AVL樹一個節點上只存了一個關鍵字(索引值)+一個磁碟地址+左右節點的引用,這是遠遠達不到16KB的,會浪費了大量的空間。

上圖中如果我們要找到6這條資料,需要進行3次IO(獲取一個節點就是一個IO操作),如果這棵樹很高的話,就會進行大量的IO操作,所以說AVL樹存在的最大問題就是空間利用不足,浪費了大量空間,資料量大的時候就會成為一顆瘦高的樹。

那麼我們可以怎麼改進呢?答案很明顯了,那就是每個磁碟塊多存一點東西,也就是說每個磁碟多存幾個關鍵字,因為關鍵字越多,路數越多;路數越多,樹也就越矮越胖,相應的操作IO次數就會越少。

多路平衡樹(Balanced Tree)

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

在這裡插入圖片描述

B樹的特點

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

  • 分叉數(路數)永遠比關鍵字數多1。

我們可以畫出如下簡圖(下圖中只畫了3路,即兩個關鍵字,實際取決於一頁能儲存多少個關鍵字):從上圖可以很明顯的看出,同樣高度的樹,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+樹到底長什麼樣呢:

在這裡插入圖片描述

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

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

InnoDB中B+樹的特點

  • 它的關鍵字的數量是跟路數相等的。

  • B+樹的根節點和枝節點中都不會儲存資料,只有葉子節點才儲存資料。而搜尋到關鍵字不會直接返回,會到最後一層的葉子節點。

  • B+樹的每個葉子節點增加了一個指向相鄰葉子節點的指標,它的最後一個數據會指向下一個葉子節點的第一個資料,形成了一個有序連結串列的結構。

  • 它是根據左閉右開的區間來檢索資料的

按照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+樹來作為索引。

點關注,不迷路

好了各位,以上就是這篇文章的全部內容了,能看到這裡的人呀,都是人才。之前說過,PHP方面的技術點很多,也是因為太多了,實在是寫不過來,寫過來了大家也不會看的太多,所以我這裡把它整理成了PDF和文件,如果有需要的可以

在這裡插入圖片描述

在這裡插入圖片描述


更多學習內容可以訪問

以上內容希望幫助到大家,很多PHPer在進階的時候總會遇到一些問題和瓶頸,業務程式碼寫多了沒有方向感,不知道該從那裡入手去提升,對此我整理了一些資料,包括但不限於:分散式架構、高可擴充套件、高效能、高併發、伺服器效能調優、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql優化、shell指令碼、Docker、微服務、Nginx等多個知識點高階進階乾貨需要的可以免費分享給大家,需要的可以加入我的