本文所述的各類數據結構(二叉樹等),均不考慮重複值的狀況,本文簡述各類數據結構的區別僅僅只是爲了理解MySQL索引的須要而作的鋪墊。mysql
提起索引,你們都知道,創建索引可讓數據庫查詢更快,那麼索引到底是什麼?我想這就不是每一個人都能說得出來了。laravel
索引,是數據庫管理系統中一個排序的數據結構,並用以協助快速查詢、 更新數據庫表中數據。sql
是的,索引是一種數據結構,可是那麼多的數據結構中爲什麼MySQL要選擇B+樹呢?接下來就讓咱們一塊兒來了解下B+樹相對於其餘數據結構有何獨特之處!shell
首先讓咱們本身想想,若是讓咱們去設計,咱們會怎麼去存儲?我想大部分人想到就是用鏈表或者數組去存儲數據,而後再按默認的順序排好,再去查找,而一個排好順序的鏈表咱們就能夠經過二分查找法來高效查詢。數據庫
二分查找也稱折半查找,是一種效率較高的查找方法。好比有1-10十個數,咱們要找到8,先從中間開始找5,而後發現8比5大,能夠把5左邊的數去掉,剩下6-10,再從中間開始找,依次類推,直到找到8爲止。可是這種查找法有一個前提是數據必須是有序的,並且這種屬於鏈表式的存儲,咱們一但要插入或者修改一個數據,可能會伴隨着大量的下標移動,好比咱們把1-10放在數組裏面,下標分別對應0-9,而後如今要插入一個0,爲了保證有序,0必須排在第一位,那麼1-10全部的數據下標都要日後移動一位,這種就有點大動干戈了,因此爲了解決這個問題,咱們就有了二叉樹。數組
二叉查找樹簡稱二叉樹(BST),英文全稱:Binary Search Tree,這是一種什麼樣的數據結構呢?請看下圖服務器
在上面這棵樹中,咱們要找到8,先從根節點6開始比較,發現8比6大,就往右邊走,就能夠找到8數據結構
二叉樹有兩個特色:架構
一、左子樹全部的節點都小於父節點併發
二、右子樹全部的節點都大於父節點
二叉樹有一個嚴重的問題,那就是它的查找耗時是和這棵樹的深度相關的,在最壞的狀況下時間複雜度會退化成 O(n)。
以下圖:
上面就是一種極端狀況下的二叉樹,會退化成線性鏈表,這種若是要找到最後一個數6,就要從1開始遍歷完整棵樹,效率就會很是低。那麼有沒有一種相對平衡一點,不要出現這種極端狀況的數據結構呢,因此就有了平衡二叉樹。
平衡二叉樹,英文全名叫作 Balanced binary search trees,簡稱AVL樹,這個AVL並非英文名的簡稱,而是發明者(G. M. Adelson-Velsky和E. M. Landis)兩我的的人名縮寫,請看下圖一個平衡二叉樹示例:
上圖中也是從1開始插入6,若是是二叉樹就會變成一種線性結構,可是平衡二叉樹就會經過左旋和右旋操做,最終會生成上圖所示的結構,感興趣的能夠進入網站本身操做觀察旋轉過程.
平衡二叉樹相比較二叉樹具備一個特色就是:左右子樹深度差絕對值不能超過 1,固然,平衡二叉樹首先是一顆二叉樹,只不過經過左旋和右旋實現左右子樹深度差不超過1,避免了二叉樹的極端狀況的出現。
既然平衡二叉樹解決了普通二叉樹的問題,那麼mysql爲什麼不選擇平衡二叉樹做爲索引呢?
讓咱們想想,若是咱們要把索引存起來,那麼應該存哪些信息呢,它應該存儲三塊信息:
索引的值:就是表裏面索引列對應的值。
數據的磁盤地址(經過磁盤地址找到當前數據)或者直接存儲整條數據。
子節點的引用:咱們須要從根節點往下走,因此須要知道左右子節點的地址。
根據這三點,能夠有以下大體的一個簡單的結構圖:
上圖中數字表示的是索引的值,0x開頭的表示磁盤地址,根節點中存了左右節點的引用。
咱們知道,頁(Page)是 Innodb 存儲引擎用於管理數據的最小磁盤單位,頁的默認大小爲16KB。頁也就是上圖中的節點,每查詢一次節點就須要進行一次IO操做,IO操做是一種很是耗時的操做,不少業務系統的瓶頸都是卡在IO操做上,因此若是咱們須要提升查詢效率的辦法之一就是減小IO次數,那麼問題就來了,AVL樹一個節點上只存了一個關鍵字(索引值)+一個磁盤地址+左右節點的引用,這是遠遠達不到16KB的,會浪費了大量的空間。
上圖中若是咱們要找到6這條數據,須要進行3次IO(獲取一個節點就是一個IO操做),若是這棵樹很高的話,就會進行大量的IO操做,因此說AVL樹存在的最大問題就是空間利用不足,浪費了大量空間,數據量大的時候就會成爲一顆瘦高的樹。
那麼咱們能夠怎麼改進呢?答案很明顯了,那就是每一個磁盤塊多存一點東西,也就是說每一個磁盤多存幾個關鍵字,由於關鍵字越多,路數越多;路數越多,樹也就越矮越胖,相應的操做IO次數就會越少。
多路平衡樹簡稱B樹,又稱B-樹,和AVL樹同樣,B樹在枝節點和葉子節點存儲鍵值、磁盤地址、左右節點引用。請看下圖的一個多路平衡樹的示例:
相比較AVL樹,B樹一個磁盤上能夠存多個關鍵字(值),並且有一個特色就是:
咱們能夠畫出以下簡圖(下圖中只畫了3路,即兩個關鍵字,實際取決於一頁能存儲多少個關鍵字):從上圖能夠很明顯的看出,一樣高度的樹,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+樹,咱們能夠發現一個很明顯的區別就是葉子節點有一個箭頭指引並且從左到右是有序的。
InnoDB中使用的B+樹相比較於傳統B+樹,改進以後的B+樹具備如下特色
它的關鍵字的數量是跟路數相等的。
B+樹的根節點和枝節點中都不會存儲數據,只有葉子節點才存儲數據。而搜索到關鍵字不會直接返回,會到最後一層的葉子節點。
B+樹的每一個葉子節點增長了一個指向相鄰葉子節點的指針,它的最後一個數據會指向下一個葉子節點的第一個數據,造成了一個有序鏈表的結構。
它是根據左閉右開的區間來檢索數據的
按照B+樹的特色,咱們能夠畫出一個存儲數據的簡圖,以下:
假設咱們如今要找一個key=66,遵循以下步驟:
一、獲取到根節點,依據左閉右開有以下區間:[1,28),[28,66),[66,+∞),命中了最後一個區間,雖然66在根節點,可是由於根節點不存儲數據,因此是會往下繼續搜索右邊的節點
二、獲取到右邊節點,依據左閉右開有以下區間:[66,78),[78,89),[89,+∞),命中左邊的範圍。
三、獲取到第三排倒數第二塊磁盤,找到66,返回數據。
B+樹是由B樹改進而來的,因此B樹能解決的問題,B+樹都能解決,那麼B+樹能解決哪些B樹所不能解決的問題呢?
一、掃庫、掃表能力更強:若是咱們要對錶進行全表掃描,只須要遍歷葉子節點就能夠 了,不須要遍歷整棵B+Tree
二、B+Tree 的磁盤讀寫能力相對於 B Tree 來講更強:根節點和枝節點不保存數據區, 因此一個節點能夠保存更多的關鍵字,一次磁盤加載(IO操做)能獲取到相對更多的關鍵字。
三、自然具有排序能力:葉子節點上有下一個數據區的指針,數據造成了鏈表。
四、效率穩定:B+Tree 永遠是在葉子節點拿到數據,因此 IO 次數是穩定的,而B樹運氣好根節點就拿到數據,運氣很差就要到葉子節點才能拿到數據,所花費的時間會有差別。
本文簡述了從二叉樹到B+樹以前的演進過程,並大體講解了各類數據結構之間的差別以及MySQL爲什麼最終會選擇了B+樹來做爲索引。
好了各位,以上就是這篇文章的所有內容了,能看到這裏的人呀,都是人才。以前說過,PHP方面的技術點不少,也是由於太多了,實在是寫不過來,寫過來了你們也不會看的太多,因此我這裏把它整理成了PDF和文檔,若是有須要的能夠
更多學習內容能夠訪問【對標大廠】精品PHP架構師教程目錄大全,只要你能看完保證薪資上升一個臺階(持續更新)
以上內容但願幫助到你們,不少PHPer在進階的時候總會遇到一些問題和瓶頸,業務代碼寫多了沒有方向感,不知道該從那裏入手去提高,對此我整理了一些資料,包括但不限於:分佈式架構、高可擴展、高性能、高併發、服務器性能調優、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql優化、shell腳本、Docker、微服務、Nginx等多個知識點高級進階乾貨須要的能夠免費分享給你們,須要的能夠加入個人 PHP技術交流羣