開門見山,直接上圖,下面的思惟導圖便是如今要講的內容,能夠先有個印象~mysql
- 常見索引類型(實現層面)
- 索引種類(應用層面)
- 聚簇索引與非聚簇索引
- 覆蓋索引
- 最佳索引使用策略
1.常見索引類型(實現層面)
首先不談Mysql怎麼實現索引的,先馬後炮一下,若是讓咱們來設計數據庫的索引,該怎麼設計?web
咱們首先思考一下索引到底想達到什麼效果?其實就是想可以實現快速查找數據的策略,因此索引的實現本質上就是一個查找算法。算法
可是跟普通的查找有所不一樣,由於咱們的數據有一下特徵:sql
1.存儲的數據是很是很是多的
2.而且還不斷的動態變化數據庫
因此實現索引時須要考慮到這兩個特色。咱們須要找一個最合適的數據結構算法來實現查找功能。緩存
下面一塊兒看下常見的查找策略,以下圖:bash
因爲前面說的兩個特色咱們首先排除靜態查找的算法。數據結構
至於查找樹,咱們有二叉樹和多叉樹兩種選擇:函數
二叉樹:若是選擇二叉樹的話,因爲咱們的數據量龐大,二叉樹的深度會變得很是大,咱們的索引樹會變成參天大樹,每次查詢會致使不少磁盤IO。性能
多叉樹:多叉樹解決了了樹的深度大的問題,那麼咱們到底選擇B樹仍是B+樹呢?
B樹 摘自維基百科 zh.wikipedia.org/wiki/B%2B樹
B+樹 摘自維基百科 zh.wikipedia.org/wiki/B%2B樹
從上面圖可知B+樹的葉子節點存放了全部的索引值,而且葉子結點之間以鏈表的形式相互關聯,因此咱們只需從最左的鏈表遍歷的話便可查找全部的值,最多見的用途就是範圍查找,而B樹則不知足這範圍查找,又或者說實現特別複雜,因此Mysql最終選擇了使用B+樹實現這一功能。
1.1 B-Tree 索引(B+樹)
先說明一下,雖然叫在Mysql官方叫作B-Tree索引,但採用的是B+樹數據結構。
B-tree索引可以加快訪問數據的速度,不須要進行全表掃描,而是從索引樹的根節點層層往下搜索,在根節點存放了索引值和指向下一個節點的指針。
下面看下單列索引的數據怎麼組織的。
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
key(`uid`)
);
複製代碼
上面User 表給uid列建立了一個索引,那麼往表裏插入uid(96~102)的時候存儲引擎是怎麼管理索引的呢?看下面的索引樹
1.在葉子節點存放全部的索引值,非葉子節點值是爲了更快定位包含目標值的葉子節點
2.葉子節點的值是有序的
3.葉子節點之間以鏈表形式關聯
下面在看一下多列(聯合)索引的數據怎麼組織的。
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
key(`uid`,`name`)
);
複製代碼
給User 表建立了聯合索引 key(uid
,name
) 這種狀況下他的索引樹是以下圖所示。
特色跟單列索引同樣,不一樣之處在於他的排序,若是第一個字段相同時會按第二個索引字段排序
如何經過B-tree快速查找數據?
對於InnoDb 存儲引擎的B-tree索引,會按一下步驟經過索引找到行數據
- 若是使用了聚簇索引(主鍵),則葉子節點上就包含行數據,可直接返回
- 若是使用了非聚簇索引(普通索引),則在葉子節點存了主鍵,再根據主鍵查詢一次上面 的聚簇索引,最後返回數據
對於MyISAM 存儲引擎的B-tree索引,會按一下步驟經過索引找到行數據
- 在MyISAM 的索引樹的葉子節點上除了索引值以外即沒存儲主鍵,也沒存儲行數據,而是存了指向行數據的指針,根據這個指針在從表文件查詢數據。
1.2 Hash 索引(哈希表)
哈希索引是基於哈希表來實現的,只有精確匹配全部的全部列才能生效。
也就是說假設有個hash索引 key (col1,col2) 那麼每次只有 col1和col2兩個字段都用纔可以生效。由於生成hash索引的時候是根據一個hash函數對全部的索引列取hash值來實現的。
以下方圖,有個hash索引key(name)
當咱們執行 mysql> select * from User where name='張三';
時怎麼利用hash索引快速查找的?
- 第一步,計算出hash值,hash(張三) = 1287
- 第二步,定位行號,好比key=1287 對應的行號爲3
- 第三步,找到指定行而且比較name列值是否爲張三作個校驗
2.常見索引種類(應用層面)
主鍵索引
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
primary key(`uid`)
);
複製代碼
主鍵索引是惟一的,一般以表的ID設置爲主鍵索引,一個表只能有一個主鍵索引,這是他跟惟一索引的區別。
惟一索引
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
unique key(`name`)
);
複製代碼
惟一索引主要用於業務上的惟一約束,他跟主鍵索引的區別是,一個表能夠有多個惟一索引
單列索引
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
key(`name`)
);
複製代碼
以某一個字段爲索引
聯合索引
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
key(`name`,`uid`)
);
複製代碼
兩個或兩個以上字段聯合組成一個索引。使用時須要注意知足最左匹配原則!
還有其餘不經常使用的就不介紹了~
3.聚簇索引與非聚簇索引
什麼是聚簇索引?
聚簇索引指的是他的 索引和行數據 在一塊兒存儲。也就是在一顆B+樹的葉子結點上存儲的不只是他的索引值,還有對應的某一行的數據。待會兒看圖便知。
聚簇索引不是一種索引,而是一種數據存儲組織方式 !!!
crreate table test( col1 int not null, col2 int not null, PRIMARY KEY(col1), KEY(col2) ); 複製代碼
如上所示,表test 由兩個索引,分別是主鍵 col1 和 普通索引 col2。那麼這倆索引跟聚簇非聚簇有啥關係呢?
會生成一個聚簇索引和一個非聚簇索引(二級索引),也就是說會組織兩個索引樹。主鍵索引會生成聚簇索引的樹 以及以col2爲索引的非聚簇索引的樹。
InnoDb 將經過主鍵來實現聚簇索引 ,若是沒有主鍵則會選選一個惟一非空索引來實現。若是沒有惟一非空索引則會隱式生成一個主鍵。
下面看下聚簇索引和非聚簇索引在索引樹上數據是怎麼分佈的,圖片摘自《高性能Nysql》
下圖是聚簇索引的數據組織方式。 col1爲主鍵索引的聚簇索引樹
索引列是主鍵 col1
能夠看出葉子結點除了存儲索引值 列col1 (3~99~4700)值 以外還存儲了其餘列的值,如列col2 (92~8~13),若是還有別的列的話也會存儲,或者換句話說聚簇索引樹 在葉子節點上存儲某個索引值對應的一行數據。
下圖是非聚簇索引(二級索引)的數據組織方式。
索引列是 col2
與聚簇索引不一樣的是非聚簇索引在索引樹葉子節點上除了索引值以外只存了主鍵值。而聚簇索引則存了一行數據。
假若有一條sql 語句 select * from test where col2=93;
上面這條語句會經歷兩次從索引樹查找過程
1.第一步從非聚簇索引的索引樹上找到包含col2=93的葉子節點,並定位到行的主鍵 3
2.第二步 根據主鍵 3 在從聚簇索引定位包含 主鍵=3的葉子節點並返回所有行數據。
以上說的都是基於InnoDb存儲引擎的,MyISAM是不支持聚簇索引的,由於他的數據文件和索引文件是相互獨立存儲的 MyISAM存儲引擎的索引樹的葉子節點不會寸主鍵值,而存一個指向對應行的地址或者說是指針,而後再從表數據文件裏去找,以下面圖所示。
結論:
-
聚簇索引:
一般由主鍵或者非空惟一索引實現的,葉子節點存儲了一整行數據 -
非聚簇索引:
又稱二級索引,就是咱們經常使用的普通索引,葉子節點存了索引值和主鍵值,在根據主鍵從聚簇索引查
4.覆蓋索引
覆蓋索引就是指索引包含了全部須要查詢的字段。
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
key(`uid`,`name`)
);
複製代碼
假如表 User有三個字段 User (name,uid,gender),且有個聯合索引 key(name,uid)那麼 執行以下面這條sql查詢時就用到了 覆蓋索引。
select name,uid from User where name in ('a','b') and uid >= 98 and uid <=100 ;
上面這條sql語句使用了聯合索引 key(name,uid),而且只需查找 name,uid兩個字段,因此使用了覆蓋索引。覆蓋索引有什麼好處呢?先看一下下面這個圖
上面這個圖就是 聯合索引key(name,uid) 所對應的索引樹,從圖中能夠看出,若是咱們只需查詢(name,uid)兩個字段的話,從索引樹就能獲得咱們須要查的數據。不須要找到索引值以後再從表數據文件定位對應的行數據了。
覆蓋索引好處
1.避免了對主鍵索引(聚簇)的二次查詢
2.因爲不須要回表查詢(從表數據文件)因此大大提高了Mysql緩存的負載
總之大大提高了讀取數據的性能
5.最佳索引使用策略
最後在講講使用索引過程當中的避坑指南
獨立的列
獨立的列不是指單列索引,而是指索引列不能是表達式的一部分或者是函數的一部分。
select * FROM test where col1 + 1 =100; // 不能是表達式一部分
select * FROM test where ABS(col1) =100; // 不能是函數一部分
最左匹配原則
假若有個聯合索引 key (col1,col2)。那麼如下查詢是索引無效的
select * from test where col2 = 3;
select * from test where col1 like '%3';
對於最左匹配原則,你們想一下B+樹的葉子節點的關聯就差很少知道爲啥須要最左匹配原則了,由於B+的葉子結點,從左到右以鏈表的形式關聯的,索引咱們查詢的時候要麼範圍查詢,要麼有明確的左邊一個開始的索引值,不能跳過或者不明確如 like '%XYZ'這種查詢。
索引值不能是null值
單列索引有null值會致使索引無效
多列索引只要有個列有null值會致使索引無效
使用聚簇索引和覆蓋索引大大提高讀取性能
由於聚簇索引和覆蓋索引的索引樹上就有了須要的字段,因此不須要回表文件查詢,因此提高了查詢速度
使用短索引若是很長的字符串進行查詢,只需匹配一個前綴長度,這樣可以節省大量索引空間