聊聊索引失效的10種場景,太坑了
執行結果:
沒錯,這次確實走了索引,恭喜被你蒙對了,因為剛好id和height欄位都建了索引。
但接下來的一個夜黑風高的晚上,需求改了:除了前面的查詢條件之後,還想加一個address='成都'。
這還不簡單,sql走起:
explain select * from user
where id=1 or height='175' or address='成都';
執行結果:
結果悲劇了,之前的索引都失效了。
你可能一臉懵逼,為什麼?我做了什麼?
答:因為你最後加的address欄位沒有加索引,從而導致其他欄位的索引都失效了。
注意:如果使用了
or
關鍵字,那麼它前面和後面的欄位都要加索引,不然所有的索引都會失效,這是一個大坑。
10. not in和not exists
在我們日常工作中用得也比較多的,還有範圍查詢,常見的有:
- in
- exists
- not in
- not exists
- between and
今天重點聊聊前面四種。
10.1 in關鍵字
假如我們想查出height在某些範圍之內的使用者,這時sql語句可以這樣寫:
explain select * from user
where height in (173,174,175,176);
執行結果:
從圖中可以看出,sql語句中用in
關鍵字是走了索引的。
10.2 exists關鍵字
有時候使用in
關鍵字時效能不好,這時就能用exists
關鍵字優化sql了,該關鍵字能達到in關鍵字相同的效果:
explain select * from user t1
where exists (select 1 from user t2 where t2.height=173 and t1.id=t2.id)
執行結果:
從圖中可以看出,用exists
關鍵字同樣走了索引。
10.3 not in關鍵字
上面演示的兩個例子是正向的範圍,即在某些範圍之內。
那麼反向的範圍,即不在某些範圍之內,能走索引不?
話不多說,先看看使用not in
的情況:
explain select * from user
where height not in (173,174,175,176);
執行結果:
你沒看錯,索引失效了。
看如果現在需求改了:想查一下id不等於1、2、3的使用者有哪些,這時sql語句可以改成這樣:
explain select * from user
where id not in (173,174,175,176);
執行結果:
你可能會驚奇的發現,主鍵欄位中使用not in關鍵字查詢資料範圍,任然可以走索引。而普通索引欄位使用了not in關鍵字查詢資料範圍,索引會失效。
10.4 not exists關鍵字
除此之外,如果sql語句中使用not exists
時,索引也會失效。具體sql語句如下:
explain select * from user t1
where not exists (select 1 from user t2 where t2.height=173 and t1.id=t2.id)
執行結果:
從圖中看出sql語句中使用not exists關鍵後,t1表走了全表掃描,並沒有走索引。
11. order by的坑
在sql語句中,對查詢結果進行排序是非常常見的需求,一般情況下我們用關鍵字:order by
就能搞定。
但我始終覺得order by挺難用的,它跟where
或者limit
關鍵字有很多千絲萬縷的聯絡,一不小心就會出問題。
Let go
11.1 哪些情況走索引?
首先當然要溫柔一點,一起看看order by的哪些情況可以走索引。
我之前說過,在code、age和name這3個欄位上,已經建了聯合索引:idx_code_age_name。
11.1.1 滿足最左匹配原則
order by後面的條件,也要遵循聯合索引的最左匹配原則。具體有以下sql:
explain select * from user
order by code limit 100;
explain select * from user
order by code,age limit 100;
explain select * from user
order by code,age,name limit 100;
執行結果:
從圖中看出這3條sql都能夠正常走索引。
除了遵循最左匹配原則之外,有個非常關鍵的地方是,後面還是加了limit
關鍵字,如果不加它索引會失效。
11.1.2 配合where一起使用
order by還能配合where一起遵循最左匹配原則。
explain select * from user
where code='101'
order by age;
執行結果:
code是聯合索引的第一個欄位,在where中使用了,而age是聯合索引的第二個欄位,在order by中接著使用。
假如中間斷層了,sql語句變成這樣,執行結果會是什麼呢?
explain select * from user
where code='101'
order by name;
執行結果:
雖說name是聯合索引的第三個欄位,但根據最左匹配原則,該sql語句依然能走索引,因為最左邊的第一個欄位code,在where中使用了。只不過order by的時候,排序效率比較低,需要走一次filesort
排序罷了。
11.1.3 相同的排序
order by後面如果包含了聯合索引的多個排序欄位,只要它們的排序規律是相同的(要麼同時升序,要麼同時降序),也可以走索引。
具體sql如下:
explain select * from user
order by code desc,age desc limit 100;
執行結果:
該示例中order by後面的code和age欄位都用了降序,所以依然走了索引。
11.1.4 兩者都有
如果某個聯合索引欄位,在where和order by中都有,結果會怎麼樣?
explain select * from user
where code='101'
order by code, name;
執行結果:
code欄位在where和order by中都有,對於這種情況,從圖中的結果看出,還是能走了索引的。
11.2 哪些情況不走索引?
前面介紹的都是正面的用法,是為了讓大家更容易接受下面反面的用法。
好了,接下來,重點聊聊order by的哪些情況下不走索引?
11.2.1 沒加where或limit
如果order by語句中沒有加where或limit關鍵字,該sql語句將不會走索引。
explain select * from user
order by code, name;
執行結果:
從圖中看出索引真的失效了。
11.2.2 對不同的索引做order by
前面介紹的基本都是聯合索引,這一個索引的情況。但如果對多個索引進行order by,結果會怎麼樣呢?
explain select * from user
order by code, height limit 100;
執行結果:
從圖中看出索引也失效了。
11.2.3 不滿足最左匹配原則
前面已經介紹過,order by如果滿足最左匹配原則,還是會走索引。下面看看,不滿足最左匹配原則的情況:
explain select * from user
order by name limit 100;
執行結果:
name欄位是聯合索引的第三個欄位,從圖中看出如果order by不滿足最左匹配原則,確實不會走索引。
11.2.4 不同的排序
前面已經介紹過,如果order by後面有一個聯合索引的多個欄位,它們具有相同排序規則,那麼會走索引。
但如果它們有不同的排序規則呢?
explain select * from user
order by code asc,age desc limit 100;
執行結果:
從圖中看出,儘管order by後面的code和age欄位遵循了最左匹配原則,但由於一個欄位是用的升序,另一個欄位用的降序,最終會導致索引失效。
好了今天分享的內容就先到這裡,我們下期再見。
最後說一句(求關注,別白嫖我)
如果這篇文章對您有所幫助,或者有所啟發的話,幫忙掃描下發二維碼關注一下,您的支援是我堅持寫作最大的動力。
求一鍵三連:點贊、轉發、在看。
關注公眾號:【蘇三說技術】,在公眾號中回覆:面試、程式碼神器、開發手冊、時間管理有超讚的粉絲福利,另外回覆:加群,可以跟很多BAT大廠的前輩交流和學習。