mysql的索引是日常開發中用到比較多的概念,對于千萬級的表來說,能充分的利用索引,便能充分的提高查詢效率。之前都是用到什么就查什么,沒有一個總體匯總的體系。索引使用的經驗真的是和自己實際的使用關系很大,以InnoDB為例簡單的總結一下:
1.索引優點
- 索引大大減小了服務器需要掃描的數據量。
- 索引可以幫助服務器避免排序和臨時表。
- 索引可以將隨機I/O變為順序I/O。
2.主鍵查詢走索引
對于索引來說,最基本的就是主鍵索引了,在建表的時候都會指定主鍵。主鍵也默認的會建立索引,我們一般使用的索引都是Btree索引,大概結構如下:
MyISAM和InnoDB索引結構有很大差異,這里以InnoDB為例,InnoDB的葉節點存儲的是數據的行,而除了主鍵之外的列索引存儲的是主鍵key,也就是說在查詢的時候需要二次查詢,先通過列索引找到主鍵,再通過主鍵索引找到row。而MyISAM的主鍵索引和列索引一致,只不過主鍵不可以重復,但是列可以。
上面的索引結構圖告訴我們,在查詢時,能使用主鍵查詢盡量不要用列查詢,因為會帶來二次查詢。
2.單個索引還是組合索引
上面我們提到一個查詢過程,其實對于普通列的查詢會轉化為兩次查詢,那么我們應該盡量能在列的查詢中過濾掉盡可能多的數據。
單個列索引查詢也有個限制,如果查詢的列不是獨立的,那么Mysql不會使用索引。獨立的列指查詢中的列不能使用表達式,也不是函數的參數。
select uname from user where rootid + 1 = 10;
上面的sql是不會走索引的,因為需要對所有的rootid列進行計算,所以要全表掃描,如果改成下面的語句則可以避免
select uname from user where rootid = 9;
所以索引列是不可以做運算的。
在老版本的Mysql中是只能用一個索引進行查詢的,在5.0之后的Mysql,引入了“索引合并”的概念。雖然能利用多個索引查詢,但是索引合并不是很合理的:
- 在做and操作的時候單個索引(組合)的性能是遠好于多個獨立索引的。而且在and時,通常只能利用多個索引中的一個進行查詢。
- 對于or操作,對于多個列索引的情況下,需要耗費大量的CPU和內存在數據的緩存、合并和排序上。
- 優化器不會把所有這些消耗計算到“查詢成本”中,導致查詢成本被遠遠低估。
所以說,其實“索引合并”只是一個優化方案,多于多個索引的掃描后計算,明顯性能上是要差于單索引的查找。
下面看一個單列查詢的例子:
這里我的mobile和uname字段都是索引字段,但是從執行計劃中可以看到只是走了uname的索引。也就是說mobile的索引是沒有用到的,那么用什么方法讓查詢也利用到mobile索引呢?
針對我們經常查詢的多列場景,我們可以建組合索引,組合索引在可以盡可能多的運用列的查詢規則。說到組合索引那么必須說一下最左前綴原則:
最左前綴原則指的的是在sql where 子句中一些條件或表達式中出現的列的順序要保持和多索引的一致或以多列索引順序出現,只要 出現非順序出現、斷層都無法利用到多列索引。
假如:現在對于一個table建立了一個組合索引(uname,age,mobile),那么對于查詢時where語句查詢條件必須為(uname,age,mobile)或(uname,age)或(uname),此時才能利用組合索引。為什么呢?
因為我們知道mysql為btree索引,對于每一個點索引數據都會維護一個(uname,age,mobile)的復合數據結構,我們在Query的時候一定會以一個字段為第一個匹配,第一個字段匹配之后選取第二個字段進行匹配依次類推。那么對于(age,mobile)、(age)、(monile)這種情況沒有第一個匹配項自然不會走索引,而(uname,mobile)也不會走索引。
下面例子的索引是(uname,mobile,email)的組合索引:
當使用(uname,mobile,email)時:
可以看出查詢全部走了索引。
當使用(uname,mobile)時:
此時也走了索引。
但是如果是(uname,email)時:
這時候只是走了uname的索引。
如果為(mobile,email)時:
此時完全沒有走索引。
4.索引的區分度
索引的區分度,主要是衡量索引值不相同的程度,區分度越大,越有利于索引的查詢。
設想一下,對于sex列,列值只有male和female,那么也就是說列中絕大多數值都是重復的,那么用此索引進行row的查找其實意義并不大。所以這樣的列建索引的意義并不大。
另一種場景,對于列值比較長的列,我們往往不能將整個列做索引,因為這樣會導致索引過大,降低索引效率。我們需要取列值的前綴進行索引,那么索引前綴的大小選擇就需要計算區分度。
索引的區分度計算主要計算是通過 不重復的索引值/數據表的總記錄數。區分度越高,索引查詢時會讓mysql在查詢時過濾掉更多的行。值越接近1,證明區分度越高。
5.組合索引的順序和區分度:
上面說到了區分度越高的越容易用來做索引,因為區分度高的列可以很容易的過濾掉很多的數據。對于組合索引來說,在考慮索引的順序的時候也是要考慮數據的分布,也就是區分度。對于多個列構成的組合索引,在查詢過濾的時候也是和列的位置有關的,這也是最左前綴規則說的事情,也就是說如果在第一次能過濾掉大量的數據,那么后續的索引匹配就能減少很多消耗。所以在選擇索引順序的時候最好是要考慮到區分度的問題,將區分度比較高的列放在前面。
6.利用索引進行排序
Mysql可以通過兩種方式達到排序的效果:
- 進行排序計算
- 按照索引順序掃描
對于后者,掃描索引是很快的。但是如果索引不能覆蓋查詢所需的全部列,那么對于每一次查詢都會回表查詢一次行。這基本都是隨機IO。這種情況下按照索引順序讀取數據反而會慢于全表掃描。
只有當索引的列順序和Order By子句的順序完全一致時,并且所有的列的排序方向都一樣時,才能使用索引對子句進行排序。
也就是說索引的排序必須保證最左前綴規則,當然也有例外,就是在where子句中指定索引列為常量,同時保證where中條件和order by中條件滿足最左前綴規則。首先看一下滿足最左前綴規則的order by:
explain select uname from testuser order by uname,mobile;
上面的表的索引是(uname, mobile, addtime),可以看到,查詢走了組合索引,同時查詢使用了覆蓋索引。
除了上述的方式,還可以這么寫:
explain select uname from testuser where uname = 'zhangsan' order by mobile,addtime;
可以 看出來Extra中沒有filesort,證明排序走了索引。
當排序條件順序不一致時:
explain select uname from testuser where uname = 'zhangsan' order by mobile asc,addtime DESC;
可以看到,Extra中出現了filesort,也就是說排序沒有用到索引,所以排序順序不一致的情況下是不能利用索引進行排序的。
上面都是針對組合索引的,單列索引對于索引排序意義不大,order by 只有在使用排序字段索引時才會有用。
針對排序這種場景,其實還可以在業務中實現排序,這樣能大大的減輕數據庫的壓力,不至于因為一個查詢而影響其他業務。
7.應該注意的幾點
- 在使用索引查詢的時候,需要保證索引類型和查詢的數據類型一致,經?;煊玫氖怯胕nt型查詢varchar類型的數據或反過來,這樣會導致索引失效。
- range查詢要盡量放在后面,因為在range后面的查詢不會走索引,這一點在設計索引的室友要注意
- Like查詢不能前綴模糊匹配,也就是說不可以like ‘%123’。因為like的后綴模糊 like ‘123%’可以轉化為range查詢,但是前綴模糊不可以。
- 索引不是越多越好,索引十分大時不僅會影響查詢效率,同時會為數據的插入造成很大的負擔。
- 對于重復索引需要刪除,規劃好索引是高效率的前提。
以上是最近學習mysql索引的筆記,如果有什么不正確的地方,懇請指出,找錯改錯的過程很重要。勿忘初心,方得始終~