MySQL索引的原理,B+樹、彙集索引和二級索引的結構分析

索引是一種用於快速查詢行的數據結構,就像一本書的目錄就是一個索引,若是想在一本書中找到某個主題,通常會先找到對應頁碼。在mysql中,存儲引擎用相似的方法使用索引,先在索引中找到對應值,而後根據匹配的索引記錄找到對應的行。html

咱們首先了解一下索引的幾種類型和索引的結構。mysql

索引類型

B樹

大多數存儲引擎都支持B樹索引。b樹一般意味着全部的值都是按順序存儲的,而且每個葉子節點到根的距離相同。B樹索引可以加快訪問數據的速度,由於存儲引擎再也不須要進行全表掃描來獲取數據。下圖就是一顆簡單的B樹。sql

B樹的查詢流程:
如上圖我要從找到E字母,查找流程以下:
(1)獲取根節點的關鍵字進行比較,當前根節點關鍵字爲M,E<M(26個字母順序),因此往找到指向左邊的子節點(二分法規則,左小右大,左邊放小於當前節點值的子節點、右邊放大於當前節點值的子節點);
(2)拿到關鍵字D和G,D<E<G 因此直接找到D和G中間的節點;
(3)拿到E和F,由於E=E 因此直接返回關鍵字和指針信息(若是樹結構裏面沒有包含所要查找的節點則返回null);
(4)經過指針信息取出這條記錄的全部信息;bash

B+樹

下圖爲B+樹的結構,B+樹是B樹的升級版,咱們能夠觀察一下,B樹和B+樹的區別是什麼?數據結構

B+樹和B樹的區別是:性能

  1. B樹的節點中沒有重複元素,B+樹有。
  2. B樹的中間節點會存儲數據指針信息,而B+樹只有葉子節點才存儲。
  3. B+樹的每一個葉子節點有一個指針指向下一個節點,把全部的葉子節點串在了一塊兒。

從下圖咱們能夠直觀的看到B樹和B+樹的區別:紫紅色的箭頭是指向被索引的數據的指針,大紅色的箭頭即指向下一個葉子節點的指針。優化

咱們假設被索引的列是主鍵,如今查找主鍵爲5的記錄,模擬一下查找的過程:spa

B樹:在倒數第二層的節點中找到5後,能夠馬上拿到指針獲取行數據,查找中止。
B+樹:在倒數第二層的節點中找到5後,因爲中間節點不存有指針信息,則繼續往下查找,在葉子節點中找到5,拿到指針獲取行數據,查找中止。3d

B+樹每一個父節點的元素都會出如今子節點中,是子節點的最大(或最小)元素。葉子節點存儲了被索引列的全部的數據。指針

那B+樹比起B樹有什麼優勢呢?

  1. 因爲中間節點不存指針,一樣大小的磁盤頁能夠容納更多的節點元素,樹的高度就小。(數據量相同的狀況下,B+樹比B樹更加「矮胖」),查找起來就更快。
  2. B+樹每次查找都必須到葉子節點才能獲取數據,而B樹不必定,B樹能夠在非葉子節點上獲取數據。所以B+樹查找的時間更穩定。
  3. B+樹的每個葉子節點都有指向下一個葉子節點的指針,方便範圍查詢和全表查詢:只須要從第一個葉子節點開始順着指針一直掃描下去便可,而B樹則要對樹作中序遍歷。

瞭解了B+樹的結構以後,咱們對一張具體的表作分析:

create table Student(
    last_name varchar(50) not null, 
    first_name varchar(50) not null, 
    birthday date not null, 
    gender int(2) not null, 
    key(last_name, first_name, birthday)
);
複製代碼

對於表中的每一行數據,索引中包含了namebirthday列的值。下圖顯示了該索引的結構:

索引對多個值進行排序的依據是create table語句中定義索引時列的順序,即若是名字相同,則根據生日來排序。

B+樹的結構決定了這種索引對如下類型的查詢有效:

全值匹配
和索引中全部的列進行匹配,例如查找姓名爲Cuba Allen,生日爲1960-01-01的人。

匹配最左前綴
查找姓爲Allen的人,即只用索引的第一列。

匹配列前綴
匹配某一列的值的開頭部分,例如查找全部以J開頭的姓的人。

匹配範圍值
查找姓在AllenBarrymore之間的人。

精確匹配某一列並範圍匹配另一列
查找姓爲Allen,名字是字母K開頭的人。即第一列last_name全匹配,第二列first_name範圍匹配。

只訪問索引的查詢
查詢只須要訪問索引,無需訪問數據行。這種索引叫作覆蓋索引。

一些限制:

  • 若是不是按照索引的最左列開始查找,沒法使用索引。例如上面例子中的索引沒法用於查找某個特定生日的人,由於生日不是最左數據列。也不能查找last_name以某個字母結尾的人。
  • 不能跳過索引的列。上述索引沒法用於查找last_nameSmith而且某個特定生日的人。若是不指定first_name,則mysql只能使用索引的第一列。
  • 若是查詢中有某個列的範圍查詢,則右邊全部的列都沒法使用索引優化查找。例如查詢WHERE last_name=’Smith’ AND first_name LIKE ‘J%’ AND birthday=‘1996-05-19’,這個查詢只能使用索引的前兩列。

哈希索引

哈希索引,只有精確匹配索引全部列的查詢纔有效。對於每一行數據,存儲引擎都會對全部的索引列計算一個哈希碼。哈希索引將全部的哈希碼存儲在索引中,同時在哈希表中保存指向每一個數據行的指針。若是多個列的哈希值相同,索引會以鏈表的方式存放多個指針記錄到同一個哈希條目中。
由於索引自身只存儲對應的哈希值,因此索引的結構十分緊湊,哈希索引查找的速度很是快。可是哈希索引也有它的限制:

  • 哈希索引不是按照索引順序存儲的,沒法用於排序。
  • 不支持部分索引列匹配查找。
  • 不支持範圍查找。

彙集索引(clusterd index)

每一個存儲引擎爲InnoDB的表都有一個特殊的索引,叫彙集索引。彙集索引並非一種單獨的索引類型,而是一種數據存儲方式。當表有彙集索引的時候,它的數據行實際上存放在葉子頁中。一個表不可能有兩個地方存放數據,因此一個表只能有一個彙集索引。
由於是存儲引擎負責實現索引,所以不是全部的存儲引擎都支持彙集索引。InnoDB表中彙集索引的索引列就是主鍵,因此彙集索引也叫主鍵索引。 例以下面這張InnoDB表:

create table Student(
    id int(11) primary key auto_increment,
    last_name varchar(50) not null, 
    first_name varchar(50) not null, 
    birthday date not null
);
複製代碼

彙集索引(主鍵索引)的結構以下圖:

這是一課B+樹,它的葉子頁包含了行的所有數據,節點頁只包含了索引列(即主鍵)。

二級索引(secondary indexes)

對於InnoDB表,在非主鍵列的其餘列上建的索引就是二級索引(由於彙集索引只有一個)。二級索引能夠有0個,1個或者多個。二級索引和彙集索引的區別是什麼呢?二級索引的節點頁和彙集索引同樣,只存被索引列的值,而二級索引的葉子頁除了索引列值,還存這一列對應的主鍵值。

InnoDB和MyISAM的數據分佈對比

如下表爲例,咱們看下InnoDB和MyISAM是如何存儲這個表的:

create table layout_test(
    col1 int(11) primary key, 
    col2 int(11) not null, 
    key(col2)
);
複製代碼

InnoDB表的數據分佈

彙集索引(主鍵索引)分佈以下:

能夠看到,葉子節點存儲了整個表的數據,而不是隻有索引列,每一個葉子節點包含了主鍵值、事務ID、用於事務和MVCC的回滾指針以及全部的剩餘列(col2)。

二級索引分佈以下:

二級索引的葉子節點中存儲的不是「行指針」,而是主鍵值,並以此做爲指向行的「指針」。這樣的策略減小了當出現行移動或者數據頁分裂時二級索引的維護工做。使用主鍵當作指針會讓二級索引佔更多空間,但好處是InnoDB在移動行時無需更新二級索引中的這個指針。

MyISAM表的數據分佈

col1列上的索引:

col2列上的索引:

實際上MyISAM中主鍵索引和其餘索引在結構上沒有什麼不一樣。

從下圖能夠看出InnoDB和MyISAM保存數據和索引的區別。

彙集索引的優勢:

  • 能夠把相關數據保存在一塊兒,例如實現電子郵箱時,根據用戶ID來彙集數據,讀取少數的數據頁就能獲取某個用戶的所有郵件。
  • 彙集索引將索引和數據保存在同一個B樹中,所以從彙集索引中獲取數據比在非彙集索引中要快一些。

彙集索引的缺點:

  • 插入速度嚴重依賴插入順序。按照主鍵的順序插入是加載數據到InnoDB表中速度最快的方式。假如磁盤中的某一個已經存滿了,可是新增的行要插入到這一頁當中,存儲引擎就會把該也分裂成兩個頁面來容納該行,這就是一次頁分裂操做。頁分裂會致使表佔用更多的磁盤空間。
  • 更新彙集索引列的代價很高,會強制InnoDB將每一個被更新的行移動到新的位置。 用二級索引訪問數據須要兩個索引查找,不是一次。由於要先從二級索引的葉子節點得到主鍵值,再根據這主鍵去彙集索引中查到對應的行,因此須要兩次B樹查找。

順序主鍵的策略:

在InnoDB表中使用自增主鍵是既簡單性能又高的策略,這樣能夠保證數據按順序寫入。最好避免隨機的彙集索引,從性能的角度考慮,使用UUID來做爲彙集索引是很糟糕的,這樣不只插入行花費的時間長,並且索引佔用的空間也更大。

參考資料:

《高性能mysql(第三版)》

stackoverflow.com/questions/8…

dev.mysql.com/doc/refman/…

www.jianshu.com/p/1f2560f0e…

相關文章
相關標籤/搜索