1. 程式人生 > 其它 >聊聊索引失效的10種場景,太坑了

聊聊索引失效的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大廠的前輩交流和學習。