最左匹配原則
最左匹配原則就是指在聯合索引中,如果你的 SQL 語句中用到了聯合索引中的最左邊的索引,那么這條 SQL 語句就可以利用這個聯合索引去進行匹配。例如某表現有索引(a,b,c),現在你有如下語句:
select * from t where a=1 and b=1 and c =1; #這樣可以利用到定義的索引(a,b,c),用上a,b,c select * from t where a=1 and b=1; #這樣可以利用到定義的索引(a,b,c),用上a,b select * from t where b=1 and a=1; #這樣可以利用到定義的索引(a,b,c),用上a,c(mysql有查詢優化器) select * from t where a=1; #這樣也可以利用到定義的索引(a,b,c),用上a select * from t where b=1 and c=1; #這樣不可以利用到定義的索引(a,b,c) select * from t where a=1 and c=1; #這樣可以利用到定義的索引(a,b,c),但只用上a索引,b,c索引用不到
也就是說通過最左匹配原則你可以定義一個聯合索引,但是使得多中查詢條件都可以用到該索引。
值得注意的是,當遇到范圍查詢(>、<、between、like)就會停止匹配。也就是:
select * from t where a=1 and b>1 and c =1; #這樣a,b可以用到(a,b,c),c索引用不到
這條語句只有 a,b 會用到索引,c 都不能用到索引。這個原因可以從聯合索引的結構來解釋。
但是如果是建立(a,c,b)聯合索引,則a,b,c都可以使用索引,因為優化器會自動改寫為最優查詢語句
select * from t where a=1 and b >1 and c=1; #如果是建立(a,c,b)聯合索引,則a,b,c都可以使用索引 #優化器改寫為 select * from t where a=1 and c=1 and b >1;
這也是最左前綴原理的一部分,索引index1:(a,b,c),只會走a、a,b、a,b,c 三種類型的查詢,其實這里說的有一點問題,a,c也走,但是只走a字段索引,不會走c字段。
另外還有一個特殊情況說明下,select * from table where a = '1' and b > ‘2’ and c='3' 這種類型的也只會有 a與b 走索引,c不會走。
像select * from table where a = '1' and b > ‘2’ and c='3' 這種類型的sql語句,在a、b走完索引后,c肯定是無序了,所以c就沒法走索引,數據庫會覺得還不如全表掃描c字段來的快。
以index (a,b,c)為例建立這樣的索引相當於建立了索引a、ab、abc三個索引。一個索引頂三個索引當然是好事,畢竟每多一個索引,都會增加寫操作的開銷和磁盤空間的開銷。
最左匹配原則的原理
最左匹配原則都是針對聯合索引來說的,所以我們可以從聯合索引的原理來了解最左匹配原則。
我們都知道索引的底層是一顆 B+ 樹,那么聯合索引當然還是一顆 B+ 樹,只不過聯合索引的健值數量不是一個,而是多個。構建一顆 B+ 樹只能根據一個值來構建,因此數據庫依據聯合索引最左的字段來構建 B+ 樹。
例子:假如創建一個(a,b,c)的聯合索引,那么它的索引樹是這樣的:
該圖就是一個形如(a,b,c)聯合索引的 b+ 樹,其中的非葉子節點存儲的是第一個關鍵字的索引 a,而葉子節點存儲的是三個關鍵字的數據。這里可以看出 a 是有序的,而 b,c 都是無序的。但是當在 a 相同的時候,b 是有序的,b 相同的時候,c 又是有序的。
通過對聯合索引的結構的了解,那么就可以很好的了解為什么最左匹配原則中如果遇到范圍查詢就會停止了。以 select * from t where a=5 and b>0 and c =1; #這樣a,b可以用到(a,b,c),c不可以
為例子,當查詢到 b 的值以后(這是一個范圍值),c 是無序的。所以就不能根據聯合索引來確定到低該取哪一行。
總結
- 在 InnoDB 中聯合索引只有先確定了前一個(左側的值)后,才能確定下一個值。如果有范圍查詢的話,那么聯合索引中使用范圍查詢的字段后的索引在該條 SQL 中都不會起作用。
- 值得注意的是,
in
和=
都可以亂序,比如有索引(a,b,c),語句select * from t where c =1 and a=1 and b=1
,這樣的語句也可以用到最左匹配,因為 MySQL 中有一個優化器,他會分析 SQL 語句,將其優化成索引可以匹配的形式,即select * from t where a =1 and a=1 and c=1
為什么要使用聯合索引
1、減少開銷。建一個聯合索引(col1,col2,col3),實際相當於建了(col1),(col1,col2),(col1,col2,col3)三個索引。每多一個索引,都會增加寫操作的開銷和磁盤空間的開銷。對於大量數據的表,使用聯合索引會大大的減少開銷!
2、覆蓋索引。對聯合索引(col1,col2,col3),如果有如下的sql: select col1,col2,col3 from test where col1=1 and col2=2。那么MySQL可以直接通過遍歷索引取得數據,而無需回表,這減少了很多的隨機io操作。減少io操作,特別的隨機io其實是dba主要的優化策略。所以,在真正的實際應用中,覆蓋索引是主要的提升性能的優化手段之一。
3、效率高。索引列越多,通過索引篩選出的數據越少。有1000W條數據的表,有如下sql:select from table where col1=1 and col2=2 and col3=3,假設假設每個條件可以篩選出10%的數據,如果只有單值索引,那么通過該索引能篩選出1000W10%=100w條數據,然后再回表從100w條數據中找到符合col2=2 and col3= 3的數據,然后再排序,再分頁;如果是聯合索引,通過索引篩選出1000w10% 10% *10%=1w,效率提升可想而知!
使用索引優化查詢問題:
1、創建單列索引還是多列索引?
如果查詢語句中的where、order by、group 涉及多個字段,一般需要創建多列索引,比如:
select * from user where nick_name = 'ligoudan' and job = 'dog';
2、多列索引的順序如何選擇?
一般情況下,把選擇性高德字段放在前面,比如:
查詢sql:
select * from user where age = '20' and name = 'zh' order by nick_name;
這時候如果建索引的話,首字段應該是age,因為age定位到的數據更少,選擇性更高。
但是務必注意一點,滿足了某個查詢場景就可能導致另外一個查詢場景更慢。
3、避免使用范圍查詢
很多情況下,范圍查詢都可能導致無法使用索引。
4、盡量避免查詢不需要的數據
explain select * from user where job like '%ligoudan%';
explain select job from user where job like '%ligoudan%';
同樣的查詢,不同的返回值,第二個就可以使用覆蓋索引,第一個只能全表遍歷了。
5、查詢的數據類型要正確
explain select * from user where create_date >= now();
explain select * from user where create_date >= '2020-05-01 00:00:00';
第一條語句就可以使用create_date的索引,第二個就不可以。