來源 | 蘇三說技術(shù)作者 | 蘇三呀前言不知道你在實際工作中,有沒有遇到過下面的這兩種情況:
今天就跟大家一起聊聊,mysql數(shù)據(jù)庫索引失效的10種場景,給曾經(jīng)踩過坑,或者即將要踩坑的朋友們一個參考。 1. 準備工作所謂空口無憑,如果我直接把索引失效的這些場景丟出來,可能沒有任何說服力。 所以,我決定建表和造數(shù)據(jù),給大家一步步演示效果,盡量做到有理有據(jù)。 我相信,如果大家耐心地看完這篇文章,一定會有很多收獲的。 1.1 創(chuàng)建user表創(chuàng)建一張user表,表中包含:
此外,還創(chuàng)建了三個索引:
1.2 插入數(shù)據(jù)為了方便給大家做演示,我特意向user表中插入了3條數(shù)據(jù):
周星馳和周杰倫是我偶像,在這里自戀了一次,把他們和我放到一起了。哈哈哈。 1.3 查看數(shù)據(jù)庫版本為了防止以后出現(xiàn)不必要的誤會,在這里有必要查一下當前數(shù)據(jù)庫的版本。不說版本就直接給結(jié)論,是耍流氓,哈哈哈。
查出當前的mysql版本號為: 1.4 查看執(zhí)行計劃在mysql中,如果你想查看某條sql語句是否使用了索引,或者已建好的索引是否失效,可以通過 例如:
執(zhí)行結(jié)果:
2. 不滿足最左匹配原則之前我已經(jīng)給code、age和name這3個字段建好 該索引字段的順序是:
如果在使用聯(lián)合索引時,沒注意最左前綴原則,很有可能導致索引失效喔,不信我們一起往下看。 2.1 哪些情況索引有效?先看看哪些情況下,能走索引。
執(zhí)行結(jié)果:
其實還有一種比較特殊的場景:
執(zhí)行結(jié)果:
看到這里,不知道聰明的你,有沒有發(fā)現(xiàn)這樣一個規(guī)律:這4條sql中都有code字段,它是索引字段中的第一個字段,也就是最左邊的字段。只要有這個字段在,該sql已經(jīng)就能走索引。 這就是我們所說的 2.2 哪些情況索引失效?前面我已經(jīng)介紹過,建立了聯(lián)合索引后,在查詢條件中有哪些情況索引是有效的。 接下來,我們重點看看哪些情況下索引會失效。
執(zhí)行結(jié)果:
說明以上3種情況不滿足最左匹配原則,說白了是因為查詢條件中,沒有包含給定字段最左邊的索引字段,即字段code。 3. 使用了select *在《阿里巴巴開發(fā)手冊》中明確說過,查詢sql中禁止使用 那么,你知道為什么嗎? 廢話不多說,按照國際慣例先上一條sql:
執(zhí)行結(jié)果:
如果查詢的時候,只查我們真正需要的列,而不查所有列,結(jié)果會怎么樣? 非??焖俚貙⑸厦娴膕ql改成只查了code和name列,太easy了:
執(zhí)行結(jié)果:
其實這里用到了: 如果select語句中的查詢列,都是索引列,那么這些列被稱為覆蓋索引。這種情況下,查詢的相關(guān)字段都能走索引,索引查詢效率相對來說更高一些。 而使用 4. 索引列上有計算介紹本章節(jié)內(nèi)容前,先跟大家一起回顧一下,根據(jù)id查詢數(shù)據(jù)的sql語句:
執(zhí)行結(jié)果:
但如果id列上面有計算,比如:
執(zhí)行結(jié)果:
5. 索引列用了函數(shù)有時候我們在某條sql語句的查詢條件中,需要使用函數(shù),比如:截取某個字段的長度。 假如現(xiàn)在有個需求:想查出所有身高是17開頭的人,如果sql語句寫成這樣:
該sql語句確實用到了普通索引:
為了滿足上面的要求,我們需要把sql語句稍稍改造一下:
這時需要用到 執(zhí)行結(jié)果:
6. 字段類型不同在sql語句中因為字段類型不同,而導致索引失效的問題,很容易遇到,可能是我們?nèi)粘9ぷ髦凶钊菀缀雎缘膯栴}。 到底怎么回事呢? 請大家注意觀察一下t_user表中的code字段,它是 在sql語句中查詢數(shù)據(jù)時,查詢條件我們可以寫成這樣:
執(zhí)行結(jié)果:
但如果你在寫sql時,不小心把引號弄掉了,把sql語句變成了:
執(zhí)行結(jié)果:
這時你心里可能有一萬個為什么,其中有一個肯定是:為什么索引會失效呢? 答:因為code字段的類型是varchar,而傳參的類型是int,兩種類型不同。 此外,還有一個有趣的現(xiàn)象,如果int類型的height字段,在查詢時加了引號條件,卻還可以走索引:
執(zhí)行結(jié)果:
這是變魔術(shù)嗎?這不科學呀。 答:mysql發(fā)現(xiàn)如果是 mysql會把上面列中的字符串175,轉(zhuǎn)換成數(shù)字175,所以仍然能走索引。 接下來,看一個更有趣的sql語句:
它的執(zhí)行結(jié)果是2,還是11呢? 好吧,不賣關(guān)子了,直接公布答案,執(zhí)行結(jié)果是2。 mysql自動把字符串1,轉(zhuǎn)換成了int類型的1,然后變成了:1+1=2。 但如果你確實想拼接字符串該怎么辦? 答:可以使用 具體拼接sql如下:
接下來,關(guān)鍵問題來了:為什么字符串類型的字段,傳入了int類型的參數(shù)時索引會失效呢? 答:根據(jù)mysql官網(wǎng)上解釋,字符串'1'、' 1 '、'1a'都能轉(zhuǎn)換成int類型的1,也就是說可能會出現(xiàn)多個字符串,對應一個int類型參數(shù)的情況。那么,mysql怎么知道該把int類型的1轉(zhuǎn)換成哪種字符串,用哪個索引快速查值? 感興趣的小伙伴可以再看看官方文檔: 7. like左邊包含%模糊查詢,在我們?nèi)粘5墓ぷ髦?,使用頻率還是比較高的。 比如現(xiàn)在有個需求:想查詢姓李的同學有哪些? 使用
但如果like用得不好,就可能會出現(xiàn)性能問題,因為有時候它的索引會失效。 不信,我們一起往下看。 目前l(fā)ike查詢主要有三種情況:
假如現(xiàn)在有個需求:想查出所有code是10開頭的用戶。 這個需求太簡單了吧,sql語句如下:
執(zhí)行結(jié)果:
而如果把需求改了:想查出所有code是1結(jié)尾的用戶。 查詢sql語句改為:
執(zhí)行結(jié)果:
此外,如果出現(xiàn)以下sql:
該sql語句的索引也會失效。 下面用一句話總結(jié)一下規(guī)律:當 那么,為什么會出現(xiàn)這種現(xiàn)象呢? 答:其實很好理解,索引就像字典中的目錄。一般目錄是按字母或者拼音從小到大,從左到右排序,是有順序的。 我們在查目錄時,通常會先從左邊第一個字母進行匹對,如果相同,再匹對左邊第二個字母,如果再相同,匹對其他的字母,以此類推。 通過這種方式我們能快速鎖定一個具體的目錄,或者縮小目錄的范圍。 但如果你硬要跟目錄的設計反著來,先從字典目錄右邊匹配第一個字母,這畫面你可以自行腦補一下,你眼中可能只剩下絕望了,哈哈。 8. 列對比上面的內(nèi)容都是常規(guī)需求,接下來,來點不一樣的。 假如我們現(xiàn)在有這樣一個需求:過濾出表中某兩列值相同的記錄。比如user表中id字段和height字段,查詢出這兩個字段中值相同的記錄。 這個需求很簡單,sql可以這樣寫:
執(zhí)行結(jié)果:
為什么會出現(xiàn)這種結(jié)果? id字段本身是有主鍵索引的,同時height字段也建了普通索引的,并且兩個字段都是int類型,類型是一樣的。 但如果把兩個單獨建了索引的列,用來做列對比時,索引會失效。 9. 使用or關(guān)鍵字我們平時在寫查詢sql時,使用 不信一起往下面看。 某天你遇到這樣一個需求:想查一下id=1或者height=175的用戶。 你三下五除二就把sql寫好了:
執(zhí)行結(jié)果:
但接下來的一個夜黑風高的晚上,需求改了:除了前面的查詢條件之后,還想加一個address='成都'。 這還不簡單,sql走起:
執(zhí)行結(jié)果:
你可能一臉懵逼,為什么?我做了什么? 答:因為你最后加的address字段沒有加索引,從而導致其他字段的索引都失效了。
10. not in和not exists在我們?nèi)粘9ぷ髦杏玫靡脖容^多的,還有范圍查詢,常見的有:
今天重點聊聊前面四種。 10.1 in關(guān)鍵字假如我們想查出height在某些范圍之內(nèi)的用戶,這時sql語句可以這樣寫:
執(zhí)行結(jié)果:
10.2 exists關(guān)鍵字有時候使用
執(zhí)行結(jié)果:
10.3 not in關(guān)鍵字上面演示的兩個例子是正向的范圍,即在某些范圍之內(nèi)。 那么反向的范圍,即不在某些范圍之內(nèi),能走索引不? 話不多說,先看看使用
執(zhí)行結(jié)果:
看如果現(xiàn)在需求改了:想查一下id不等于1、2、3的用戶有哪些,這時sql語句可以改成這樣:
執(zhí)行結(jié)果:
10.4 not exists關(guān)鍵字除此之外,如果sql語句中使用
執(zhí)行結(jié)果:
11. order by的坑在sql語句中,對查詢結(jié)果進行排序是非常常見的需求,一般情況下我們用關(guān)鍵字: 但我始終覺得order by挺難用的,它跟 11.1 哪些情況走索引?首先當然要溫柔一點,一起看看order by的哪些情況可以走索引。 我之前說過,在code、age和name這3個字段上,已經(jīng)建了聯(lián)合索引:idx_code_age_name。 11.1.1 滿足最左匹配原則order by后面的條件,也要遵循聯(lián)合索引的最左匹配原則。具體有以下sql:
執(zhí)行結(jié)果:
除了遵循最左匹配原則之外,有個非常關(guān)鍵的地方是,后面還是加了 11.1.2 配合where一起使用order by還能配合where一起遵循最左匹配原則。
執(zhí)行結(jié)果:
假如中間斷層了,sql語句變成這樣,執(zhí)行結(jié)果會是什么呢?
執(zhí)行結(jié)果:
11.1.3 相同的排序order by后面如果包含了聯(lián)合索引的多個排序字段,只要它們的排序規(guī)律是相同的(要么同時升序,要么同時降序),也可以走索引。 具體sql如下:
執(zhí)行結(jié)果:
11.1.4 兩者都有如果某個聯(lián)合索引字段,在where和order by中都有,結(jié)果會怎么樣?
執(zhí)行結(jié)果:
11.2 哪些情況不走索引?前面介紹的都是正面的用法,是為了讓大家更容易接受下面反面的用法。 好了,接下來,重點聊聊order by的哪些情況下不走索引? 11.2.1 沒加where或limit如果order by語句中沒有加where或limit關(guān)鍵字,該sql語句將不會走索引。
執(zhí)行結(jié)果:
11.2.2 對不同的索引做order by前面介紹的基本都是聯(lián)合索引,這一個索引的情況。但如果對多個索引進行order by,結(jié)果會怎么樣呢?
執(zhí)行結(jié)果:
11.2.3 不滿足最左匹配原則前面已經(jīng)介紹過,order by如果滿足最左匹配原則,還是會走索引。下面看看,不滿足最左匹配原則的情況:
執(zhí)行結(jié)果:
11.2.4 不同的排序前面已經(jīng)介紹過,如果order by后面有一個聯(lián)合索引的多個字段,它們具有相同排序規(guī)則,那么會走索引。 但如果它們有不同的排序規(guī)則呢?
執(zhí)行結(jié)果:
- END - |
|