索引對於MySQL來說是一個非常重要的東西,在特定情況下能有效地提升MySQL的查詢效率,但如果使用不當則會降低效率。所以我在這裏記錄一下這幾天學習的MySQL索引優化的知識
使用索引有幾個需要非常注意的點
1、全值匹配
2、最佳左前綴法則
3、不在索引列上做任何操作(計算、函數(自動或手動)類型轉換)
4、存儲引擎不能使用索引中範圍條件右邊的列
5、儘量使用覆蓋索引(只訪問索引的查詢,也就是索引列和查詢列一致),減少select * 的使用
6、MySQL在使用不等於(!=或<>)的時候無法使用索引會導致全表掃描(存在爭議,是否正確?詳細請往下看)
7、is null、is not null 無法使用索引(存在爭議,是否正確?詳細請往下看)
8、使用 like 時以通配符開頭(‘%abc’)會導致索引失效變成全表掃描的操作
9、字符串不加單引號會導致索引失效
10、少用 or,使用 or 連接時會導致索引失效
接下來就一個個詳細說明
一、全值匹配
全值匹配沒什麼好說的,和正常的條件查找相似
// 創建索引
create index idx_name_age_deptId on emp(name,age,deptId)
由於我們的索引爲name,age,deptId,而我們的查詢條件也包含索引且是固定值,所以可以成功使用索引。
重點:因爲我們建立的索引順序爲name,age,deptId,如果條件不按順序會怎麼樣?
還是可以正常使用索引!!
原因:MySQL中的優化器會優化我們where之後的sql語句
在這個例子中會將我們的 WHERE deptId=2 AND emp.name=‘abc’ AND age=30
優化爲 WHERE emp.name=‘abc’ AND age=30 AND deptId=2
使得sql語句中的條件順序符合索引順序,通過使用索引提高查詢效率
二、最佳左前綴法則
按全值匹配來看,這三條查詢語句是完全沒有問題的,但 第一個查詢語句並沒有用到索引,而 第三句只用到了索引中的name
這是因爲查詢字段與索引字段的個數不同的前提下,順序不同,導致索引無法充分使用,甚至索引失效
使用複合索引,需要遵循最佳左前綴法則,即如果索引了多列,需要遵循最佳左前綴法則。指的是查詢從索引的最左前列開始並且不跳過索引中的列
結論:過濾條件要使用索引必須按照索引建立時的順序,依次滿足,一旦跳過某個字段,索引後面的字段都無法被使用
三、不在索引列上做任何操作(計算、函數(自動或手動)類型轉換)
如果違背了這個原則,會導致索引失效而轉向全表掃描
1)在查詢列上使用了函數
可以看到在查詢列上使用了函數之後,索引失效,變爲了全表掃描
結論:等號左邊不能有計算
2)在查詢列上做了轉換
原本name是varchar類型的,但如果我們查詢時使用3000而不是使用’3000’,MySQL會自動幫我們在name列上做一次轉換,使索引失效,變爲全表掃描
結論:等號右邊不能有類型(自動或手動)轉換
四、存儲引擎不能使用索引中範圍條件右邊的列
可以看到原本查詢類型是ref,但條件變成範圍後查詢變成了range級別,導致查詢效率降低
結論:在設置索引時,將可能做範圍查詢的字段索引順序放到最後
五、儘量使用覆蓋索引(只訪問索引的查詢,也就是索引列和查詢列一致),減少select * 的使用
即 查詢列和索引列相同,不要寫select *
可以看到Extra中出現了Using index,說明我們的sql得到了優化
六、MySQL在使用不等於(!=或<>)的時候無法使用索引會導致全表掃描
這裏存在一個爭議,就是關於!=使索引失效對不對的問題,詳細請看第七點
七、關於is null、is not null 使用索引
字段允許爲null時:
但這樣是否就說明了is not null用不到索引,is null可以用到索引呢?(!=也有相同的問題)
答案並不是,MySQL中決定使不使用某個索引執行查詢的依據就是成本夠不夠小,如果null值很多,還是會用到索引的,如果這個條數佔整個記錄條數的比例特別大,那麼就趨向於使用全表掃描執行查詢,否則趨向於使用這個索引執行查詢
如果在索引列上改條件爲 Is Not Null ,因爲索引列的所有非空值都存儲在索引中,按道理也是可以走索引的。但是,爲了解析查詢語句,優化程序需要從索引中讀取每一個值,在映射到表中索引返回的行。
在大多數情況下,執行全表掃描比爲索引返回的所有值執行索引掃描(相關的Table Access By Index Rowid操作)效率更高
有一點非常重要,通過觀察SQL語句而推斷執行計劃是很不現實的,需要綜合考察SQL語句所涉及表的索引、數據分佈、統計信息,才能綜合判斷,用通俗的話來說要結合具體場景
結論:如果謂詞上面建立有索引的話,基本上都會走索引,至於是走索引查找還是索引掃描與索引類型有一定關係,也與字段位於聯合索引中位置有關係。另外,數據分佈傾斜得非常厲害也會導致其走全表掃描而不走索引,但是這並不是說IS NULL 和 IS NOT NULL導致索引失效
附上兩篇參考博客
MySQL中 IS NULL、IS NOT NULL、!= 能用上索引嗎?
SQL SERVER 中is null 和 is not null 將會導致索引失效嗎?
八、使用 like 時以通配符開頭(‘%abc’)會導致索引失效變成全表掃描的操作
結論:前綴不能出現模糊匹配
九、字符串不加單引號會導致索引失效
雖然說前面提到過了,但這一條非常重要,很有可能在你沒有意識到的情況下拖慢你程序的速度,所以再拿出來提一嘴
詳細見第三條
十、少用 or,使用 or 連接時會導致索引失效
結論:可以使用 union all 或者 union 來代替 or
小結
往下看之前先記住一句話:分組之前必排序
假設index(a,b,c,d)
Where語句 | 索引是否被使用 |
---|---|
where a=3 | 使用到a |
where a=3 and b=5 | 使用到a、b |
where a=3 and b=5 and c=6 | 使用到a、b、c |
where a=3 or b=5 or c=6 | 索引不被使用 |
where a=3 and c=6 | 使用到a,但c沒被使用,因爲沒有b,中間斷了 |
where c=6 and a=3 and b=5 | 使用到a、b、c |
where a=3 and b>5 and c=6 | 使用到a和b,c不能用在範圍之後 |
where a=3 and c>5 and b=5 | 使用到a、b、c |
where abs(a)=3 | 索引不被使用 |
where a=3 and b=5 and d=7 order by c | 查找索引只用到了a、b,索引c被用於排序 |
where a=3 and b=5 order by c | (與上一條類比,與d無關) |
where a=3 and b=5 order by d | 查找索引只用到了a、b,並且Extra中出現了Using filesort |
where a=3 and d=7 order by b,c | 查找索引只用到了a,索引b、c被用於排序,無filesort |
where a=3 and d=7 order by c,b | (類比上一條)出現Using filesort(MySQL不會order by語句順序) |
where a=3 and b=5 and d=7 order by c,b | (類比上一條)無filesort(此時b=5,在order by中b可以當成一個常量) |
where a=3 and d=7 group by b,c | 只使用了a,無filesort |
where a=3 and d=7 group by c,b | (類比上一條)只使用了a,出現filesort和temporary |
where a=3 and b like ‘kk%’ and c=4 | 使用到a、b、c |
where a=3 and b like ‘%kk’ and c=4 | (類比上一條)只使用到a |
where a=3 and b like %‘kk%’ and c=4 | (類比上一條)只使用到a |
where a=3 and b like ‘k%kk%’ and c=4 | (類比上一條)使用到a、b、c |
定值爲常量,範圍後失效,最終看排序
一般order by是給個範圍,group by基本上都需要進行排序,會有臨時表產生
建議:
- 對於單鍵索引,儘量選擇針對當前query過濾性更好的索引
- 在選擇組合索引時,當前query中過濾性最好的子彈在索引字段順序中位置越靠前越好
- 在選擇組合索引時,儘量選擇可以包含當前query中where子句中更多字段的索引
附贈一個索引優化口訣:
全值匹配我最愛,最左前綴要遵守
帶頭大哥不能死,中間兄弟不能斷
索引列上少計算,範圍之後全失效
like 百分寫最右,覆蓋索引不寫 *
不等空值還有or,索引影響要注意
varchar引號不能丟,SQL優化有訣竅