where條件中出現(xiàn)了(不等于/多重范圍)就一定不走索引了嗎

今天朋友拋了一個群里的討論給我,如下圖

這題我選擇了A,B,因為我認(rèn)為A和B都不滿足最左;最后朋友實際建表的測試結(jié)果如下(也就是說答案應(yīng)該是A,B,D)

按理說不等于不走索引應(yīng)該是SQL優(yōu)化的常識了,為什么我在選擇的時候排除了D呢?因為2個月前我遇到了這樣一個語句

SELECT MAX(id)

FROM a

WHERE user_id != '18'

AND '2019-02-13 00:00:00' >= create_time

GROUP BY user_id;

因為這個語句的關(guān)系,我一度認(rèn)為在5.7.25版本里不等于也已經(jīng)開始智能的走索引了;經(jīng)過這個選擇題,決定仔細(xì)探索一下MySQL的不等于


對原語句進(jìn)行不同的改寫

備注:

①索引idx_uid_ct為user_id和create_time的聯(lián)合索引idx_uid_ct(user_id,create_time)

②后面為了測試,特意刪除了idx_ct(create_time)

③id為表的主鍵列

eg1.去除group by的影響,type為range,覆蓋索引

eg2.去除max函數(shù),type為range,覆蓋索引

eg3.查詢項改為create_time、user_id、id中的任意1到3個,type為range,覆蓋索引

eg4.查詢項改為*或者是索引列(user_id,create_time,id)之外的任意值,索引前導(dǎo)列存在不等于,不走索引

eg5.查詢項改為*或者是索引列(user_id,create_time,id)之外的任意值,索引的非前導(dǎo)列存在不等于,走索引,extra為ICP


eg6.查詢項改為create_time或者user_id,where條件改為只有非前導(dǎo)列create_time,不滿足idx_uid_ct最左匹配規(guī)則,type為index,覆蓋索引

eg7.查詢項改為索引列(user_id,create_time,id)之外的任意值,where條件將user_id的不等于改為等于,create_time條件不變,type為range,Extra為ICP

eg8.查詢項改為索引列(user_id,create_time,id)之外的任意值,where條件將user_id的不等于改為大于或者小于,create_time條件不變,是否走索引需要優(yōu)化器進(jìn)行判斷

eg9.查詢項改為索引列(user_id,create_time,id)之中的任意1-3個值,where條件將user_id的不等于改為大于或者小于,create_time條件不變,type為range,覆蓋索引


結(jié)論

①對比上面的9個eg可以看出,當(dāng)查詢列位于主鍵或者索引列范圍內(nèi)的時候(或者像原語句那樣是主鍵/索引列的分組函數(shù)),整個語句會直接走覆蓋索引,這種情況下無論where條件里有沒有不等于都不會有影響

②參考eg4可知查詢列在索引列或主鍵列的范圍之外,索引前導(dǎo)列存在不等于,不走索引

③參考eg5可知,查詢列在索引列或主鍵列的范圍之外,索引非前導(dǎo)列存在不等于,仍然走索引,此時需要回表,因此extra為ICP。

④對比eg5和eg7,兩者的執(zhí)行計劃中的extra均為ICP,但key_len和type不同;eg5的非前導(dǎo)列在where條件中存在不等于,因此聯(lián)合索引中的非前導(dǎo)列create_time并沒有被利用到,所以key_len只有99;eg7的非前導(dǎo)列條件where條件中是一個范圍,由于是where條件中的第一個范圍,因此這個范圍可以走索引,所以key_len為105;eg5和eg7之所以都是ICP是因為查詢列user_name需要回表

⑤參考eg9,當(dāng)滿足覆蓋索引條件的時候,即使在where條件中有雙范圍,第一個范圍走了索引,第二個范圍沒有走索引(key_len為99)

⑥參考eg8,不滿足覆蓋索引條件時,where條件中只有第一個范圍能被索引利用到,是否走索引優(yōu)化器自身會做權(quán)衡,當(dāng)取值量太大時索引反而不如全表

eg6是一個我不能理解的eg,滿足覆蓋索引條件時,即使where條件不滿足索引的最左規(guī)則,語句仍然走了索引只不過type為極為低效的index(全索引掃描),extra顯示此時仍然是覆蓋索引;有趣的是這里的key_len竟然是105,而實際上where條件里面只有create_time(該列的key_len應(yīng)該只有6才對)


綜上可以知道在覆蓋索引的情況下,不等于仍然會走索引(無論這個不等于是否存在于前導(dǎo)列的where條件上),而多重范圍的第一個范圍會走索引,而后面的多重范圍則不會走索引,只是用來作為過濾條件使用;

在非覆蓋的索引下,不等于不會走索引(如果是前導(dǎo)列存在不等于,這個索引直接不會走;如果是非前導(dǎo)列存在不等于,這個索引有可能被用到,但即使索引被用到,索引中這個存在不等于的列也不會被用到),而多重范圍的后幾層范圍肯定不會被用到,第一層范圍是否會用到需要優(yōu)化器進(jìn)行權(quán)衡


補充

SELECT sum( amount ) FROM t

WHERE

STATUS = 'success'

AND partner_id != 'aaa' AND partner_id != 'bbb' AND partner_id != 'ccc'

AND date_format( update_time, '%y-%m-%d' ) = date_format( now( ), '%y-%m-%d' );

建立測試索引

idx_0(status,partner_id,update_time,amount)

idx_1(status,partner_id,amount)

①在采用idx_1時,索引只走了idx_1中的status部分,這時候存在不等于條件的partner_id只是起到了過濾的作用(這個過濾作用使得該語句執(zhí)行速度提升10倍)

②在采用idx_0時,索引走了idx_0中的status和partner_id部分,這時候因為已經(jīng)是覆蓋查詢了,根據(jù)前面的實驗可以知道不等于也走了索引,但是最末端的update_time仍然沒有走索引,而只是起到了過濾的作用(這個過濾作用使得該語句在之前提升10倍的基礎(chǔ)上再提升了10倍)

結(jié)論

在覆蓋索引的情況下,不等于會走索引,多重范圍不會走索引,函數(shù)也不會走索引

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容