聯合索引在B+樹上的存儲結構及數據查找方式

能堅持別人不能堅持的,才能擁有別人不曾擁有的。
關注 編程大道公衆號,讓咱們一同堅持心中所想,一塊兒成長!!

引言

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

  • 聯合索引在B+樹上的存儲結構
  • 聯合索引的查找方式
  • 爲何會有最左前綴匹配原則


在分享這篇文章以前,我在網上查了關於MySQL聯合索引在B+樹上的存儲結構這個問題,翻閱了不少博客和技術文章,其中有幾篇講述的與事實相悖。慶幸的是看到搜索引擎列出的有一條是來自思否社區的問答,有答主回答了這個問題,貼出一篇文章和一張圖以及一句簡單的描述。PS:貼出的文章連接已經打不開了。
因此在這樣的條件下這篇文章就誕生了。編程

聯合索引的存儲結構

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


聯合索引 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部分存儲主鍵值上篇文章也有介紹,感興趣或還不知道的能夠去看一下。spa

好了大體狀況都介紹完了。下面咱們結合這倆圖來解釋一下。3d


對於聯合索引來講只不過比單值索引多了幾列,而這些索引列全都出如今索引樹上。對於聯合索引,存儲引擎會首先根據第一個索引列排序,如上圖咱們能夠單看第一個索引列,如,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列值相等的狀況下這個小範圍內遞增,如第一葉子節點的第一、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還根據不一樣引擎支持的哈希索引、全文索引等等本文也並未涉及。若是有機會,但願再對本文未涉及的部分進行補充吧。
創做不易,若是對你有幫助,請不要吝嗇你的贊,這對我是很大的鼓勵~

 


~~手機閱讀的用戶請掃碼關注公衆號,你的關注是對我最大的支持!~~

 

 

 

 


以爲好看,請點這裏

相關文章
相關標籤/搜索