深入理解MySQL索引的底層數據結構和算法

原文鏈接:https://blog.csdn.net/u010922732/article/details/82992920

目錄

一 理解索引的特性

二 索引的各種存儲結構及其優缺點

(一) 二叉樹

(二) 紅黑樹

(三) Hash

(四) B-Tree

(五) B+Tree(MySQL索引的真正存儲結構)

三. 聯合索引底層存儲結構


一 理解索引的特性

  • 索引是幫助MySQL高效獲取數據的排好序數據結構
  • 索引存儲在文件裏

二 索引的各種存儲結構及其優缺點

在開始講這一小節之前,我們先來看一下在數據庫沒有加索引的情況下,SQL中的where字句是如何查找目標記錄的。

我們先看下左邊表格第二列Col2列的數據時如何查找的,如果我們希望查找where Col2 = 22的記錄,我們在沒加索引的情況下是按順序從第一條記錄查找,由此可知需要查找5次才能找到;

如果對Col2字段加上索引後,我們假設使用最簡單的二叉樹作爲索引存儲方式,再次查找where Col2 = 22的記錄這次只需要查找2次就能找到目標記錄,效率提高十分明顯。

(一) 二叉樹

1. 優點:

二叉樹是一種比順序結構更加高效地查找目標元素的結構,它可以從第一個父節點開始跟目標元素值比較,如果相等則返回當前節點,如果目標元素值小於當前節點,則移動到左側子節點進行比較,大於的情況則移動到右側子節點進行比較,反覆進行操作最終移動到目標元素節點位置。

2. 缺點:

在大部分情況下,我們設計索引時都會在表中提供一個自增整形字段作爲建立索引的列,在這種場景下使用二叉樹的結構會導致我們的索引總是添加到右側,在查找記錄時跟沒加索引的情況是一樣的,如下圖所示:

(二) 紅黑樹

1. 優點:

紅黑樹也叫平衡二叉樹,它不僅繼承了二叉樹的優點,而且解決了上面二叉樹遇到的自增整形索引的問題,從下面的動態圖中可以看出紅黑樹會走動對結構進行調整,始終保證左子節點數 < 父節點數 < 右子節點數的規則。

2. 缺點:

在數據量大的時候,深度也很大。從圖中可以看出每個父節點只能存在兩個子節點,如果我們有很多數據,那麼樹的深度依然會很大,可能就會超過十幾二十層以上,對我們的磁盤尋址不利,依然會花費很多時間查找。

(三) Hash

1. 優點:

對數據進行Hash(散列)運算,主流的Hash算法有MD5、SHA256等等,然後將哈希結果作爲文件指針可以從索引文件中獲得數據的文件指針,再到數據文件中獲取到數據,按照這樣的設計,我們在查找where Col2 = 22的記錄時只需要對22做哈希運算得到該索引所對應那行數據的文件指針,從而在MySQL的數據文件中定位到目標記錄,查詢效率非常高。

2. 缺點:

無法解決範圍查詢(Range)的場景,比如 select count(id) from sus_user where id >10;因此Hash這種索引結構只能針對字段名=目標值的場景使用。

不適合模糊查詢(like)的場景。

(四) B-Tree

既然紅黑樹存在缺點,那麼我們可以在紅黑樹的基礎上構思一種新的儲存結構。解決的思路也很簡單,既然覺得樹的深度太長,就只需要適當地增加每個樹節點能存儲的數據個數即可,但是數據個數也必須要設定一個合理的閾值,不然一個節點數據個數過多會產生多餘的消耗。

按照這樣的思路,我們先來了解下關於B-Tree的一些知識點:

  • 度(Degree)-節點的數據存儲個數,每個樹節點中數據個數大於 15/16*Degree(未驗證) 時會自動分裂,調整結構
  • 葉節點具有相同的深度,左子樹跟右子樹的深度一致
  • 葉節點的指針爲空
  • 節點中的數據key從左到右遞增排列

1. 樹節點結構:

在這裏需要說明下的是,BTree的結構裏每個節點包含了索引值和表記錄的信息,我們可以按照Map集合這樣理解:key=索引,value=表記錄,如下圖所示:

2. 優點:

BTree的結構可以彌補紅黑樹的缺點,解決數據量過大時整棵樹的深度過長的問題。相同數量的數據只需要更少的層,相同深度的樹可以存儲更多的數據,查找的效率自然會更高。

3. 缺點:

從上面得知,在查詢單條數據是非常快的。但如果範圍查的話,BTree結構每次都要從根節點查詢一遍,效率會有所降低,因此在實際應用中採用的是另一種BTree的變種B+Tree(B+樹)。

(五) B+Tree(MySQL索引的真正存儲結構)

在介紹B+Tree之前,我們先來看下面兩個問題:

1. 爲什麼要對BTree繼續做優化?

要解答這個疑問需要先了解BTree每個節點結構(上面已經說明)和MySQL數據庫它是如何讀取索引數據的,索引和表數據在不使用的時候是存儲在文件中的,也就是磁盤,當我們執行查詢操作時會DBMS(數據庫管理系統)首先會先從內存中查找,如果找到直接使用,如果找不到則從磁盤文件中讀取;操作系統儲存數據的最小單位是頁(page),一頁假設是4K大小(由操作系統決定),對內存和磁盤讀取數據是按一頁的整數倍讀取的。

這裏我們假設數據庫一次IO操作就讀取1頁4K的數據,再假設圖中圈起來的元素就是一個大節點,內含多個小節點的索引和數據,其大小是10MB,那麼我們要從磁盤中讀取完整個大節點需要進行 10M / 4K = 2500次IO操作,這樣就可以看出如果大節點數據總量越大,需要執行的IO操作越多,花費的時間也越長,因此爲了提高性能,數據庫會建議我們一個大節點只存儲一頁4K大小的數據,這裏的數據包含了索引和表記錄,另外我們還能計算出樹的度Degree應該設置成多大才合理:

Degree = 內存頁大小(4K) / 單個索引值字節大小;

進一步分析,索引值的大小相對於整條記錄的大小是很小的,如果我們需要查找的數據剛好是在最後,那麼前面遍歷過的節點中存儲的記錄數據是不是對我們來說是沒用的,它會佔用比索引大得多的空間,導致我們一個大節點裏能遍歷的索引數量大大減少,需要向下繼續遍歷的機率就更大,花費更多時間查找,那麼有沒有辦法可以優化呢?看下一個問題。

2. 相對於BTree,B+Tree做了哪些優化?

  • B+Tree存儲結構,只有葉子節點存儲數據

新的B+樹結構沒有在所有的節點裏存儲記錄數據,而是隻在最下層的葉子節點存儲,上層的所有非葉子節點只存放索引信息,這樣的結構可以讓單個節點存放下更多索引值,增大度Degree的值,提高命中目標記錄的機率。

這種結構會在上層非葉子節點存儲一部分冗餘數據,但是這樣的缺點都是可以容忍的,因爲冗餘的都是索引數據,不會對內存造成大的負擔。

  • 每個葉子節點都指向下一個葉子節點

這點優化有什麼用呢?我們直接看下面的B+Tree結構,如果我們進行範圍查找where id > 4的記錄,我們只需要先找到id = 4的記錄後自然就能通過葉子節點間的雙向指針方便地查詢出大於4的所有記錄。

三. 聯合索引底層存儲結構

單列索引其實也可以看做聯合索引,索引列爲1的聯合索引,從下圖就可以看出聯合索引的底層存儲跟單列索引時類似的,區別在於聯合索引是每個樹節點中包含多個索引值,在通過索引查找記錄時,會先將聯合索引中第一個索引列與節點中第一個索引值進行匹配,匹配成功接着匹配第二個索引列和索引值,直到聯合索引的所有索引列都匹配完;如果過程中出現某一個索引列與節點相應位置的索引值不匹配的情況,則無需再匹配節點中剩餘索引列,前往下一個節點。

感謝大家的閱讀,本文如有錯誤的地方,希望能私信我改正,共同進步!

下一篇文章打算介紹兩種常見的數據庫存儲引擎InnoDB和MyISAM

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章