索引是一種用於快速查詢行的數據結構,就像一本書的目錄就是一個索引,若是想在一本書中找到某個主題,通常會先找到對應頁碼。在mysql中,存儲引擎用相似的方法使用索引,先在索引中找到對應值,而後根據匹配的索引記錄找到對應的行。html
咱們首先了解一下索引的幾種類型和索引的結構。mysql
大多數存儲引擎都支持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樹和B+樹的區別:紫紅色的箭頭是指向被索引的數據的指針,大紅色的箭頭即指向下一個葉子節點的指針。優化
咱們假設被索引的列是主鍵,如今查找主鍵爲5的記錄,模擬一下查找的過程:spa
B樹:在倒數第二層的節點中找到5後,能夠馬上拿到指針獲取行數據,查找中止。
B+樹:在倒數第二層的節點中找到5後,因爲中間節點不存有指針信息,則繼續往下查找,在葉子節點中找到5,拿到指針獲取行數據,查找中止。3d
B+樹每一個父節點的元素都會出如今子節點中,是子節點的最大(或最小)元素。葉子節點存儲了被索引列的全部的數據。指針
那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)
);
複製代碼
對於表中的每一行數據,索引中包含了name
,birthday
列的值。下圖顯示了該索引的結構:
索引對多個值進行排序的依據是create table
語句中定義索引時列的順序,即若是名字相同,則根據生日來排序。
B+樹的結構決定了這種索引對如下類型的查詢有效:
全值匹配
和索引中全部的列進行匹配,例如查找姓名爲Cuba Allen
,生日爲1960-01-01
的人。
匹配最左前綴
查找姓爲Allen
的人,即只用索引的第一列。
匹配列前綴
匹配某一列的值的開頭部分,例如查找全部以J開頭的姓的人。
匹配範圍值
查找姓在Allen
和Barrymore
之間的人。
精確匹配某一列並範圍匹配另一列
查找姓爲Allen
,名字是字母K開頭的人。即第一列last_name
全匹配,第二列first_name
範圍匹配。
只訪問索引的查詢
查詢只須要訪問索引,無需訪問數據行。這種索引叫作覆蓋索引。
一些限制:
last_name
以某個字母結尾的人。last_name
爲Smith
而且某個特定生日的人。若是不指定first_name
,則mysql只能使用索引的第一列。WHERE last_name=’Smith’ AND first_name LIKE ‘J%’ AND birthday=‘1996-05-19’
,這個查詢只能使用索引的前兩列。哈希索引,只有精確匹配索引全部列的查詢纔有效。對於每一行數據,存儲引擎都會對全部的索引列計算一個哈希碼。哈希索引將全部的哈希碼存儲在索引中,同時在哈希表中保存指向每一個數據行的指針。若是多個列的哈希值相同,索引會以鏈表的方式存放多個指針記錄到同一個哈希條目中。
由於索引自身只存儲對應的哈希值,因此索引的結構十分緊湊,哈希索引查找的速度很是快。可是哈希索引也有它的限制:
每一個存儲引擎爲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+樹,它的葉子頁包含了行的所有數據,節點頁只包含了索引列(即主鍵)。
對於InnoDB表,在非主鍵列的其餘列上建的索引就是二級索引(由於彙集索引只有一個)。二級索引能夠有0個,1個或者多個。二級索引和彙集索引的區別是什麼呢?二級索引的節點頁和彙集索引同樣,只存被索引列的值,而二級索引的葉子頁除了索引列值,還存這一列對應的主鍵值。
如下表爲例,咱們看下InnoDB和MyISAM是如何存儲這個表的:
create table layout_test(
col1 int(11) primary key,
col2 int(11) not null,
key(col2)
);
複製代碼
彙集索引(主鍵索引)分佈以下:
能夠看到,葉子節點存儲了整個表的數據,而不是隻有索引列,每一個葉子節點包含了主鍵值、事務ID、用於事務和MVCC的回滾指針以及全部的剩餘列(col2)。
二級索引分佈以下:
二級索引的葉子節點中存儲的不是「行指針」,而是主鍵值,並以此做爲指向行的「指針」。這樣的策略減小了當出現行移動或者數據頁分裂時二級索引的維護工做。使用主鍵當作指針會讓二級索引佔更多空間,但好處是InnoDB在移動行時無需更新二級索引中的這個指針。
col1列上的索引:
col2列上的索引:
實際上MyISAM中主鍵索引和其餘索引在結構上沒有什麼不一樣。
從下圖能夠看出InnoDB和MyISAM保存數據和索引的區別。
在InnoDB表中使用自增主鍵是既簡單性能又高的策略,這樣能夠保證數據按順序寫入。最好避免隨機的彙集索引,從性能的角度考慮,使用UUID來做爲彙集索引是很糟糕的,這樣不只插入行花費的時間長,並且索引佔用的空間也更大。
參考資料:
《高性能mysql(第三版)》