MySQL的索引是一個非常重要的知識點,也基本上是面試必考的一個技術點,所以非常重要。那你瞭解MySQL索引的資料結構是怎麼樣的嗎?為什麼要採用這樣的資料結構?
現在化身為MySQL的架構師,一步步迭代設計出MySQL的索引結構,保證你再也忘記不了索引的結構了,輕鬆通過面試。
MySQL表中儲存的資料量非常大,可能有上億條記錄,如果一條條去匹配,就是所謂的全表掃描,會非常的慢。那麼有什麼辦法呢?
想想我們生活中的例子,比如新華字典,我們有一個目錄,目錄根據拼音排序,內容包含了漢字位於字典中具體的的頁碼。聰明的你肯定也想到了,我們也可以借鑑這種思想,建立一個MySQL的目錄,叫做「索引」。
所以你對「索引」做了抽象和定義:索引(Index)是幫助MySQL高效獲取資料的資料結構。
索引是在儲存引擎中實現的,因此每種儲存引擎的索引不一定完全相同,MySQL有InnoDB、MyISAM、Memory等儲存引擎,你想了下,就拿最常用的InnoDB作為儲存引擎設計索引。
你現在拼命轉動大腦,開始去思考如何設計出這樣的一個索引結構。你就在腦子裡想,索引設計中需要解決哪些問題,以及要達成什麼樣的目標。
二分法
快速找到對應的資料記錄。16kb
, 每次滿了,就會重新有新的一頁。我的索引目錄資料應該也是放到頁中,而且索引的資料儘量少些,這樣每頁可以放更多的目錄資訊。帶著這些問題和思考,你開始設計啦。
你想著我就拿一個例子具象化的思考設計索引。
下面是一個新建的表:
CREATE TABLE demo(
c1 INT,
c2 INT,
c3 CHAR(1),
PRIMARY KEY(c1)
) ROW_FORMAT = Compact;
行記錄的格式簡化如下:
我們只在示意圖裡展示記錄的這幾個部分:
record_type
:記錄頭資訊的一項屬性,表示記錄的型別, 0 表示普通記錄、 2 表示最小記錄、 3 表示最大記錄、 1 暫時還沒用過,下面講。next_record
:記錄頭資訊的一項屬性,表示下一條地址相對於本條記錄的地址偏移量,我們用箭頭來表明下一條記錄是誰。把一些記錄放到頁裡的示意圖就是:
注意:一頁可以存放16kb的資料,並不是圖上的3條資料,這裡只是一個範例。
我們為什麼要遍歷所有的資料頁或者記錄?因為各個頁中的記錄並沒有規律,不知道這條資料出現在哪個資料頁中。那麼如何快速定位要查詢的資料在哪個資料頁中呢?我們需要建立一定的規律,如下:
查詢主鍵值為 20 的記錄,具體查詢過程分兩步:
先從目錄項中根據二分法快速確定出主鍵值為 20 的記錄在 目錄項3 中(因為 12 < 20 < 209 ),它對應的頁是頁9。
再根據前邊說的在頁中查詢記錄的方式去頁9中定位具體的記錄。
迭代一中的目錄項是怎麼儲存的呢?我們是不是也可以用行記錄格式儲存到資料頁中呢。答案是肯定的,我們通過行記錄格式中的record_type
等於1表示是目錄記錄,如下圖所示:
現在以查詢主鍵為 20 的記錄為例,根據某個主鍵值去查詢記錄的步驟就可以大致拆分成下邊兩步:
先到儲存目錄項記錄的頁,也就是頁30中通過二分法快速定位到對應目錄項,因為 12 < 20 < 209 ,所以定位到對應的記錄所在的頁就是頁9。
再到儲存使用者記錄的頁9中根據二分法快速定位到主鍵值為20的使用者記錄。
隨著資料量變多,勢必一個目錄項存放不下,因為一頁只有16kb大小,就會分裂出多頁,如下圖所示:
那麼現在查詢主鍵值為 20 的記錄,流程如下:
我們現在的儲存目錄項記錄的頁有兩個,即頁30和頁32 ,又因為頁30表示的目錄項的主鍵值的 範圍是 [1, 320) ,頁32表示的目錄項的主鍵值不小於 320 ,所以主鍵值為 20 的記錄對應的目錄項記錄在頁30中。
通過目錄項記錄頁確定使用者記錄真實所在的頁。
在真實儲存使用者記錄的頁中定位到具體的記錄。
如果我們表中的資料非常多則會產生很多儲存目錄項記錄的頁,如果直接這麼查,也是很慢,我們是不是可以針對目錄項記錄的頁再生成一個更高階的目錄,就像是一個多級目錄一樣,如下圖所示:
那麼現在查詢主鍵值為 20 的記錄,流程如下:
生成了一個儲存更高階目錄項的頁33,這個頁中的兩條記錄分別代表頁30和頁32, 主鍵20的記錄在 [1, 320) 之間,則到頁30中查詢更詳細的目錄項記錄。
在頁30中通過二分法查詢主鍵為20記錄的使用者記錄頁碼。
在真實儲存使用者記錄的頁中定位到具體的記錄。
以上這個資料結構就是我們索引最終的資料結構,B+樹, 圖形描述如下:
16kb
,只有目錄資訊越少,每頁存放的資訊越多,樹的層級就越小,樹的層級越小,那麼和磁碟的IO就越少,查詢就會越快。一般來說,B+樹4層,就可以存放上億資料了。我們按照前面的迭代推演出了基於主鍵的索引結構,是一顆B+樹,我們把這種索引叫做聚簇索引。
特點:
既然有了聚簇索引,那麼肯定有非聚簇索引,非聚簇索引也叫二級索引或者輔助索引。
它是在什麼場景出現的呢?比如我們想以別的列作為搜尋條件,總不能是從頭到尾沿著連結串列依次遍歷記錄一遍,肯定要慢死了。這時候就需要建立非聚簇索引,那它的索引結構和聚簇索引有什麼區別呢?
那可能你有疑問了,只有主鍵值,我要查記錄的其他資訊怎麼辦呢?
我們根據這個以c2列大小排序的B+樹只能確定我們要查詢記錄的主鍵值,所以如果我們想根 據c2列的值查詢到完整的使用者記錄的話,仍然需要到 聚簇索引 中再查一遍,這個過程稱為 回表 。也就 是根據c2列的值查詢一條完整的使用者記錄需要使用到 2 棵B+樹!
回表的過程會耗時,為什麼不直接存放所有的資料記錄呢?
如果把完整的使用者記錄放到葉子結點是可以不用回表。但是太佔地方了,相當於每建立一課B+樹都需要把所有的使用者記錄再都拷貝一遍,這就有點太浪費儲存空間了。
聯合索引是一種特殊的非聚簇索引,那麼它的資料結構又是怎麼樣的呢?
比方說我們想讓B+樹按照c2和c3列的大小進行排序,為c2和c3建立的索引的示意圖如下:
我們在瞭解了索引的資料結構以後,就更加明白索引的優缺點了。
本為讓你親身作為一個MySQL架構師的身份,一步步帶你理解MySQL中索引的資料結構,現在是不是理解的很透徹了,如果對你有幫助的話,請留下一個贊吧。
更多學習資料請移步:程式設計師成神之路
本文來自部落格園,作者:JAVA旭陽,轉載請註明原文連結:https://www.cnblogs.com/alvinscript/p/16967780.html