SQL優化 MySQL版 - 避免索引失效原則(二)
避免索引失效原則(二)
注:繼上一篇文章繼續講解:
避免索引失效原則(一)https://www.cnblogs.com/StanleyBlogs/p/10482048.html#4195062
作者 : Stanley 羅昊
【轉載請註明出處和署名,謝謝!】
體驗SQL優化中的概率情況
在上一篇文章結尾處,我們在執行查詢計劃的時候,卻發現我明明加了索引,並且也滿足了使用索引的條件,但是,給我的優化結果卻是失敗,從而,得出一個結論便是,優化是概率的,也就跟彩票一樣,不可能百分之百優化成功的,但是彩票我們都知道,全憑運氣,但是這裡就不一樣了,我們需要了解SQL優化概率背後到底是誰導致它優化失敗的;
首先,我們來了解下,出現概率優化的原因: 因為在SQL底層中,有一個服務層,服務層有一個SQL優化器,當我們寫一條語句,雖然我們手動優化了,但是,優化器覺得你優化的不太合適,它可能會進行一些自己的干擾,干擾完畢之後就執行結果就不再是你理想中的那樣了,所以這個優化器有的時候會阻擾我們的優化工作;
接下來,我們就通過幾個例子來體驗一下我們設想的優化和實際不一樣的一些操作;
首先,我們需要建立一個複合索引:
alter table book add index idx_book_at(authorid,typeid);
建立完索引後,我們進行一個簡單的查詢:
explain select * from book where authorid = 1 and typeid = 2;
通過結果我們可以發現, 複合索引全部生效了 ;
那麼接下來,我們將體驗一下讓它產生概率問題,我把上面的SQL語句拿過來改改:
explain select * from book where authorid > 1 and typeid = 2;
我們檢視執行結果:
結果很明顯, 給authorid 添加了一個大於號,這樣則導致了右側索引全部失效,包括自身,從而得出一個結論,複合索引中如果有>,則自身已經後面的索引都將會失效;
但是,這次我SQL語句再次改變,奇怪的事情將會發生:
explain select * from book where authorid = 1 and typeid > 2;
這次我把這個大於號加給了typeid欄位,顯然它也是索引,剛才我說了,新增大於號會導致自身並且右側索引全部失效,但是接下來:
現在我們又發現,結論又不對了,我明明自身肯定失效啊,為啥這次偏偏卻兩個都生效了?
原因就是概率情況,咱們在實際執行時,複合索引全部使用了,並不是剛才我們說的那個結論,自身失效及右側全部失效,當然,這個情況是大部分情況下都是有用了,僅有小部分情況會出現;
明顯的概率問題
剛才我寫了幾個例子看起來不是特別的明顯,下面我將寫幾個比較明顯的例子來體驗一下概率問題;
首先,我們編寫一條SQL語句:
explain select * from book where authorid < 1 and typeid = 2;
此時,我把authorid改成了小於號,我們看結果:
我們看到了,此時, 我們換層了小於號,發現沒有全部失效,此條語句得出結論,兩個索引,僅生效了一個因為範圍查詢僅對自身生效,對後面的不會生效 ;
接下來,我再改變一下SQL語句:
explain select * from book where authorid < 4 and typeid = 2;
首先看清楚, 我現在沒有更改任何符號,僅把authorid小於號後面的數字條件寫成了4 ,再來看看執行結果:
我們驚奇的發現,竟然全部失效了, 我明明就光改了一個數字而已,就全部失效了 ,剛才還有一個生效,現在一個都沒有了,這到底是為什麼呢?
通過後兩個例子我們發現,就改了一個數,索引都不一樣了,所以,這就是SQL優化的一個概率;
因此得出結論,我們學習的索引優化,是一個大部分情況都適用的結論,但由於SQL優化器等原因,該結論不是100%正確,因為SQL的底層把我們寫的語句給干擾了;
一般而言,範圍查詢(> < in),之後的索引失效,僅對自身生效;
補救
那麼,如果這樣一直干擾下去,我們到底還優不優化了?就沒有辦法來補救這個概率問題嗎?答案是有的;
儘量使用索引覆蓋 (using index) 在Extra裡面出現這個,就表示你的SQL語句不會出錯,如果你怕在優化中出現概率問題,那麼你就 朝著using index這個方向去優化,因為,出現這個就代表你這條SQL100%生效,不會出現概率問題;
比如我現在有 a b c三張表;
現在我編寫一條SQL,select a,b,c from 表名 where a = ... and b = ...;
在select後面我們用到了abc 並且查詢條件也是a b 沒有跨列,滿足最佳做字首,最主要的是查詢條件也是索引,所有的索引你都按照規則全部用上了,這樣就會出現索引覆蓋,大大的提高了系統性能;
like儘量以“常量”開頭,不要以'%'開頭,否則索引失效
我現在編寫一條SQL語句;
select * from 表名 where name like '%x%';
首先,這條sql語句是查詢表名中name 帶有x的資料,如果你這樣寫了,如果name是索引,那麼name將會失效!
接下來,我結合資料庫進行證實一下;
explain select tnamefrom teacher where tname like '%x%'
首先,tname我是加了一個索引的,但是看一下看一下執行結果:
沒有失效,因為出現了覆蓋索引,因為tname是索引,我剛好去查tname,所以出現了覆蓋索引,導致本次查詢沒有失效,下面我把它換成“*”;
值得注意的是,在開發過程中,嚴禁出現“*”!本次為了說明問題,所以換成“*”;
執行結果:
索引全部失效!原因我剛才也說過了,在模糊查詢是, 不要以百分號開頭 ;
如果想避免失效,可以變成以下這種寫法:
explain select tnamefrom teacher where tname like 'x%'
這樣雖然可以保證索引不會失效,但是,我們在專案開發中,難免遇到模糊查詢,所以也是有解決方案的;
剛才我不小心也試出來了,因為我使用了索引覆蓋,你想用模糊查詢可以,但是你需要有索引覆蓋,剛才我查詢tname,tname本身就在索引裡面,所以出現了索引覆蓋;
如果必須使用模糊查詢,那麼就把查詢條件以及需要查詢的欄位全部宣告成索引即可;
儘量不要使用型別轉換(顯示、隱式),否則索引失效
這裡我就簡單的舉個例子:
select * from teacher where tname = 'abc';
此時,tname是varchar型別,這個時候你你卻寫成int型別:
select * from teacher where tname = 123;
人家本來需要單引號的字串型別,結果你給人家弄了一個去掉引號的int型別,所以索引就會失效;
儘量不要使用or,否則索引失效
select * from teacher where tname = " " or tcid>1;
這條sql語句就會導致索失效,所以要避免使用or這個關鍵字!
經過測試發現,or回導致以左的索引失效,也就是tname這個欄位的索引失效了;
今日感悟:
努力就一定會有收穫,心無旁騖