MySQL索引優化指南

索引對於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基本上都需要進行排序,會有臨時表產生

建議:
  1. 對於單鍵索引,儘量選擇針對當前query過濾性更好的索引
  2. 在選擇組合索引時,當前query中過濾性最好的子彈在索引字段順序中位置越靠前越好
  3. 在選擇組合索引時,儘量選擇可以包含當前query中where子句中更多字段的索引

附贈一個索引優化口訣:

全值匹配我最愛,最左前綴要遵守
帶頭大哥不能死,中間兄弟不能斷
索引列上少計算,範圍之後全失效
like 百分寫最右,覆蓋索引不寫 *
不等空值還有or,索引影響要注意
varchar引號不能丟,SQL優化有訣竅

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章