1. 程式人生 > >聯合索引在B+樹上的儲存結構及資料查詢方式

聯合索引在B+樹上的儲存結構及資料查詢方式

能堅持別人不能堅持的,才能擁有別人未曾擁有的。
關注程式設計大道公眾號,讓我們一同堅持心中所想,一起成長!!

引言

上一篇文章《MySQL索引那些事》主要講了MySQL索引的底層原理,且對比了B+Tree作為索引底層資料結構相對於其他資料結構(二叉樹、紅黑樹、B樹)的優勢,最後還通過圖示的方式描述了索引的儲存結構。但都是基於單值索引,由於文章篇幅原因也只是在文末略提了一下聯合索引,並沒有大篇幅的展開討論,所以這篇文章就單獨去講一下聯合索引在B+樹上的儲存結構。
本文主要講解的內容有:

  • 聯合索引在B+樹上的儲存結構
  • 聯合索引的查詢方式
  • 為什麼會有最左字首匹配原則


在分享這篇文章之前,我在網上查了關於MySQL聯合索引在B+樹上的儲存結構這個問題,翻閱了很多部落格和技術文章,其中有幾篇講述的與事實相悖。慶幸的是看到搜尋引擎列出的有一條是來自思否社群的問答,有答主回答了這個問題,貼出一篇文章和一張圖以及一句簡單的描述。PS:貼出的文章連結已經打不開了。

所以在這樣的條件下這篇文章就誕生了。

聯合索引的儲存結構

下面就引用思否社群的這個問答來展開我們今天要討論的聯合索引的儲存結構的問題。
來自思否的提問,聯合索引的儲存結構(https://segmentfault.com/q/1010000017579884)有碼友回答如下:


聯合索引 bcd , 在索引樹中的樣子如圖 , 在比較的過程中 ,先判斷 b 再判斷 c 然後是 d ,


由於回答只有一張圖一句話,可能會讓你有點看不懂,所以我們就藉助前人的肩膀用這個例子來更加細緻的講探尋一下聯合索引在B+樹上的儲存結構吧。


首先,表T1有欄位a,b,c,d,e,其中a是主鍵,除e為varchar其餘為int型別,並建立了一個聯合索引idx_t1_bcd(b,c,d),然後b、c、d三列作為聯合索引,在B+樹上的結構正如上圖所示。聯合索引的所有索引列都出現在索引數上,並依次比較三列的大小。上圖樹高只有兩層不容易理解,下面是假設的表資料以及我對其聯合索引在B+樹上的結構圖的改進。PS:基於InnoDB儲存引擎。

                                                                                         bcd聯合索引在B+樹上的結構圖

                                    T1表


通過這倆圖我們心裡對聯合索引在B+樹上的儲存結構就有了個大概的認識。下面用我的語言為大家解釋一下吧。


我們先看T1表,他的主鍵暫且我們將它設為整型自增的(PS:至於為什麼是整型自增上篇文章有詳細介紹這裡不再多說),InnoDB會使用主鍵索引在B+樹維護索引和資料檔案,然後我們建立了一個聯合索引(b,c,d)也會生成一個索引樹,同樣是B+樹的結構,只不過它的data部分儲存的是聯合索引所在行的主鍵值(上圖葉子節點紫色背景部分),至於為什麼輔助索引data部分儲存主鍵值上篇文章也有介紹,感興趣或還不知道的可以去看一下。

好了大致情況都介紹完了。下面我們結合這倆圖來解釋一下。


對於聯合索引來說只不過比單值索引多了幾列,而這些索引列全都出現在索引樹上。對於聯合索引,儲存引擎會首先根據第一個索引列排序,如上圖我們可以單看第一個索引列,如,1 1 5 12 13....他是單調遞增的;如果第一列相等則再根據第二列排序,依次類推就構成了上圖的索引樹,上圖中的1 1 4 ,1 1 5以及13 12 4,13 16 1,13 16 5就可以說明這種情況。

聯合索引的查詢方式

當我們的SQL語言可以應用到索引的時候,比如 select * from T1 where b = 12 and c = 14 and d = 3; 也就是T1表中a列為4的這條記錄。儲存引擎首先從根節點(一般常駐記憶體)開始查詢,第一個索引的第一個索引列為1,12大於1,第二個索引的第一個索引列為56,12小於56,於是從這倆索引的中間讀到下一個節點的磁碟檔案地址,從磁碟上Load這個節點,通常伴隨一次磁碟IO,然後在記憶體裡去查詢。當Load葉子節點的第二個節點時又是一次磁碟IO,比較第一個元素,b=12,c=14,d=3完全符合,於是找到該索引下的data元素即ID值,再從主鍵索引樹上找到最終資料。

最左字首匹配原則

之所以會有最左字首匹配原則和聯合索引的索引構建方式及儲存結構是有關係的。
首先我們建立的idx_t1_bcd(b,c,d)索引,相當於建立了(b)、(b、c)(b、c、d)三個索引,看完下面你就知道為什麼相當於建立了三個索引。
我們看,聯合索引是首先使用多列索引的第一列構建的索引樹,用上面idx_t1_bcd(b,c,d)的例子就是優先使用b列構建,當b列值相等時再以c列排序,若c列的值也相等則以d列排序。我們可以取出索引樹的葉子節點看一下。
索引的第一列也就是b列可以說是從左到右單調遞增的,但我們看c列和d列並沒有這個特性,它們只能在b列值相等的情況下這個小範圍內遞增,如第一葉子節點的第1、2個元素和第二個葉子節點的後三個元素。
由於聯合索引是上述那樣的索引構建方式及儲存結構,所以聯合索引只能從多列索引的第一列開始查詢。所以如果你的查詢條件不包含b列如(c,d)、(c)、(d)是無法應用快取的,以及跨列也是無法完全用到索引如(b,d),只會用到b列索引。
這就像我們的電話本一樣,有名和姓以及電話,名和姓就是聯合索引。在姓可以以姓的首字母排序,姓的首字母相同的情況下,再以名的首字母排序。

如:

M    

  毛 不易   178********   

  馬 化騰   183********   

  馬 雲       188********

Z   

  張 傑      189********   

  張 靚穎   138********   

  張 藝興   176********  

我們知道名和姓是很快就能夠從姓的首字母索引定位到姓,然後定位到名,進而找到電話號碼,因為所有的姓從上到下按照既定的規則(首字母排序)是有序的,而名是在姓的首字母一定的條件下也是按照名的首字母排序的,但是整體來看,所有的名放在一起是無序的,所以如果只知道名查詢起來就比較慢,因為無法用已排好的結構快速查詢。

到這裡大家是否明白了為啥會有最左字首匹配原則了吧。

實踐

如下列舉一些SQL的索引使用情況

 1 select * from T1 where b = 12 and c = 14 and d = 3;-- 全值索引匹配 三列都用到
 2 
 3 select * from T1 where b = 12 and c = 14 and e = 'xml';-- 應用到兩列索引
 4 
 5 select * from T1 where b = 12 and e = 'xml';-- 應用到一列索引
 6 
 7 select * from T1 where b = 12  and c >= 14 and e = 'xml';-- 應用到一列索引及索引條件下推優化
 8 
 9 select * from T1 where b = 12  and d = 3;-- 應用到一列索引  因為不能跨列使用索引 沒有c列 連不上
10 
11 select * from T1 where c = 14  and d = 3;-- 無法應用索引,違背最左匹配原則

後記

到這裡MySQL索引的聯合索引的儲存結構及查詢方式就講完了,本人能力有限,也是站著前人的肩膀上創作的此文,因為看到搜尋引擎的搜尋結果前幾個技術文章中有存在講述不清或講述有誤的地方,所以自己才總結出這篇文章分享給大家,如有不對的地方一定要指正哦,謝謝了。


這篇文章斷斷續續利用工作之餘畫圖加寫作用了兩三天,主要內容就是上面這些了。不可否認,這篇文章在一定程度上有紙上談兵之嫌,因為我本人對MySQL的使用屬於菜鳥級別,更沒有太多資料庫調優的經驗,在這裡高談闊論實屬慚愧。就當是我個人的一篇學習筆記了。


另外,MySQL索引及知識非常廣泛,本文只是涉及到其中一部分。如與排序(ORDER BY)相關的索引優化及覆蓋索引(Covering index)的話題本文並未涉及,同時除B-Tree索引外MySQL還根據不同引擎支援的雜湊索引、全文索引等等本文也並未涉及。如果有機會,希望再對本文未涉及的部分進行補充吧。
創作不易,如果對你有幫助,請不要吝嗇你的贊,這對我是很大的鼓勵~

 


~~手機閱讀的使用者請掃碼關注公眾號,你的關注是對我最大的支援!~~

 

 

 

 


覺得好看,請點這裡↓↓&d