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

最困難的事情就是認識本身!

我的網站,歡迎訪問!java

前言:

本篇文章主要是闡述下 聯合索引 在 B+Tree 上的實際存儲結構。

本文主要講解的內容有:git

  • 聯合索引在B+樹上的存儲結構
  • 聯合索引的查找方式
  • 爲何會有最左前綴匹配原則
在分享這篇文章以前,我在網上查了關於MySQL聯合索引在B+樹上的存儲結構這個問題,翻閱了不少博客和技術文章,其中有幾篇講述的與事實相悖。具體以下:

不少博客中都是說:聯合索引在B+樹上的 非葉子節點 中只會存儲 聯合索引 中的第一個索引字段 的值,聯合索引的其他索引字段的值只會出如今 B+樹 的 葉子節點 中 。(其實這句話是不對的)github

以下圖,就是 錯誤的 聯合索引的 B+樹 存儲結構圖:sql

慶幸的是經過不斷查詢發現有一條是來自思否社區的關於【聯合索引 在 B+Tree 上的存儲結構?】問答,有答主回答了這個問題,並貼出了一篇文章和一張圖以及一句簡單的描述。PS:貼出的文章連接已經打不開了。

因此在這樣的條件下本篇文章就誕生了。數據庫

聯合索引存儲結構:

下面就引用思否社區的這個問答來展開咱們今天要討論的聯合索引的存儲結構的問題。

來自思否的提問,聯合索引的存儲結構
(https://segmentfault.com/q/10...
有碼友回答以下:segmentfault

聯合索引 bcd , 在索引樹中的樣子以下圖 , 在比較的過程當中 ,先判斷 b 再判斷 c 而後是 d :緩存

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

首先,有一個T1表, 而後表T1有字段a,b,c,d,e,其中a是主鍵,除e爲varchar其他爲int類型,並建立了一個聯合索引idx_t1_bcd(b,c,d),而後b、c、d三列做爲聯合索引,在B+樹上的結構正如上圖所示。聯合索引的全部索引列都出如今索引數上,並依次比較三列的大小。上圖樹高只有兩層不容易理解,下面是假設的表數據以及我對其聯合索引在B+樹上的結構圖的改進。 PS:基於InnoDB存儲引擎。優化

index(b、c、d)聯合索引在B+樹上的結構圖以下:網站

T1表中的數據以下圖:( 上圖 B+樹 中的數據就來自下圖

經過這倆圖咱們內心對聯合索引在B+樹上的存儲結構就有了個大概的認識。下面用個人語言爲你們解釋一下吧。

咱們先看T1表,他的主鍵暫且咱們將它設爲整型自增的 ,InnoDB會使用主鍵索引在B+樹維護索引和數據文件,而後咱們建立了一個聯合索引(b,c,d)也會生成一個索引樹,一樣是B+樹的結構,只不過它的 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. 存儲引擎首先從根節點(通常常駐內存)開始查找,第一個索引的第一個索引列爲1,12大於1,第二個索引的第一個索引列爲56,12小於56,因而從這倆索引的中間讀到下一個節點的磁盤文件地址(此處其實是存在一個指針的,指向的是下一個節點的磁盤位置)。
  2. 進行一次磁盤IO,將此節點值加載後內存中,而後根據第一步同樣進行判斷,發現 數據都是匹配的,而後根據指針將此聯合索引值所在的葉子節點也從磁盤中加載後內存,此時又發生了一次磁盤IO,最終根據葉子節點中索引值關聯的 主鍵值
  3. 根據主鍵值 回表 去主鍵索引樹(聚簇索引)中查詢具體的行記錄。

聯合索引的最左前綴原則:

之因此會有最左前綴匹配原則和聯合索引的索引構建方式及存儲結構是有關係的。

首先咱們建立的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的索引使用狀況:

select * from T1 where b = 12 and c = 14 and d = 3;-- 全值索引匹配 三列都用到
select * from T1 where b = 12 and c = 14 and e = 'xml';-- 應用到兩列索引
select * from T1 where b = 12 and e = 'xml';-- 應用到一列索引
select * from T1 where b = 12  and c >= 14 and e = 'xml';-- 應用到一列索引及索引條件下推優化
select * from T1 where b = 12  and d = 3;-- 應用到一列索引  由於不能跨列使用索引 沒有c列 連不上
select * from T1 where c = 14  and d = 3;-- 沒法應用索引,違背最左匹配原則

後記:

到這裏MySQL索引的聯合索引的存儲結構及查找方式就講完了,本人能力有限,也是站着前人的肩膀上創做的此文,由於看到搜索引擎的搜索結果前幾個技術文章中有存在講述不清或講述有誤的地方,因此本身才總結出這篇文章分享給你們,若有不對的地方必定要指正哦,謝謝了。

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

另外,MySQL索引及知識很是普遍,本文只是涉及到其中一部分。如與排序(ORDER BY)相關的索引優化及覆蓋索引(Covering index)的話題本文並未涉及,同時除B-Tree索引外MySQL還根據不一樣引擎支持的哈希索引、全文索引等等本文也並未涉及。若是有機會,但願再對本文未涉及的部分進行補充吧。

❤不要忘記留下你學習的足跡 [點贊 + 收藏 + 評論]嘿嘿ヾ

一切看文章不點贊都是「耍流氓」,嘿嘿ヾ(◍°∇°◍)ノ゙!開個玩笑,動一動你的小手,點贊就完事了,你每一個人出一份力量(點贊 + 評論)就會讓更多的學習者加入進來!很是感謝! ̄ω ̄=
相關文章
相關標籤/搜索