為何MySQL會選錯索引

在實際的生產(chǎn)環(huán)境中,給MySQL的字段加索引讓查詢的效率更高,我們往往會給MySQL的表字段加索引。MySQL具體選擇哪個索引是有MySQL的優(yōu)化器決定的。不知道小伙伴們有沒有遇到一種情況,就是實際的查詢中速度應(yīng)該很快才對的,但因為MySQL選錯的索引,導(dǎo)致查詢的速度變慢了。至此,我們來看一下下面的例子。

牛刀小試

首先,我們簡單創(chuàng)建一張表,表里面有兩個字段,分別是a和b,并且我們給a和b加上索引,SQL語句執(zhí)行如下:

CREATE TABLE `t` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `a` (`a`),
 KEY `b` (`b`)
) ENGINE=InnoDB

接下來我們使用存儲過程或者是跑代碼的形式往表里插入10萬條數(shù)據(jù),此處使用存儲過程,代碼如下所示:

delimiter ;;
create procedure idata()
begin
declare i int;
set i=1;
while(i<=100000)do
insert into t values(i, i, i);
set i=i+1;
end while;
end;;
delimiter ;
call idata();

然后我們使用執(zhí)行計劃運行一條簡單的SQL語句,代碼如下:

explain select * from t where a between 15000 and 20000

運行之后,如下所示:

為何MySQL會選錯索引

可以看到,執(zhí)行這條語句的時候,MySQL使用了a上面的索引,只掃描(row)了5000條記錄。想必這樣的結(jié)果大家都是可以猜到的。

在這里,也許小伙伴們就會有疑問了,MySQL是怎么計算出row數(shù)的?其實,MySQL大部分時候并不能很精準(zhǔn)的統(tǒng)計出這個row數(shù),MySQL在這里主要采用的方式是抽樣的方式進行計算的,即 row數(shù) = (N個數(shù)據(jù)頁不同值個數(shù)平均值) ×索引 頁數(shù)。一個索引上不同的值越多,這個索引的區(qū)分度就越好。而一個索引上的不同值的個數(shù)就被稱為基數(shù)。所以,row數(shù)其實嚴(yán)格意義上來說是不會100%精準(zhǔn)的。

那么,我們再執(zhí)行如下的SQL語句,看結(jié)果如何,代碼如下:

explain select * from t where a between 5000 and 15000 and b between 10000 and 100000 order by b limit 100

其結(jié)果如下所示:

為何MySQL會選錯索引

從這次的分析可以看出,MySQL最后選擇了b索引,掃描了50233行數(shù)據(jù)。從這結(jié)果來看,我們不難猜出MySQL選錯索引了。在MySQL中,order by 和 limit 結(jié)合使用,如果where 字段,order by字段上都有索引,那么有l(wèi)imit時索引會使用order by字段所在的索引,沒有l(wèi)imit會使用where 條件的索引。造成這種原因主要是b字段上面存在索引,所以MySQL的優(yōu)化器認(rèn)為使用b索引可以避免排序,直接遍歷然后取出相應(yīng)的limit的條數(shù)就可以了,所以這里MySQL掃描的行數(shù)雖然多,但仍然被優(yōu)化器認(rèn)為是代價最小的。

在這里的話,由于where條件中a的范圍要比b的要小,所以,這里如果使用a作為索引的話,肯定查詢的速度會比b的快,所以,我們執(zhí)行如下的SQL語句,進行分析下:

explain select * from t force index(a) where a between 5000 and 15000 and b between 10000 and 100000 order by b limit 100;

其結(jié)果如下圖所示:

為何MySQL會選錯索引

從兩圖可以看出,使用a索引所消耗的時間要比b索引所消耗的時間少。

優(yōu)化

其實,MySQL在查詢的過程中,大部分時候索引的選擇都是正確的,只有偶爾會遇到以上的情況,那么,如何解決以上問題的發(fā)生呢?

1. 使用force index的形式來矯正MySQL索引的選擇。不過,挺多程序員實際中并不會使用強制索引,主要是因為如果索引改了名字,這個語句也得改,顯得很麻煩。而且如果以后遷移到別的數(shù)據(jù)庫的話,這個語法還可能會不兼容

2. 修改MySQL語句,引導(dǎo)優(yōu)化器去選擇索引。例如在上面的例子中,可以把 order by b limit 100 改成 order by b,a limit 100,這樣就意味著兩個索引都要排序,優(yōu)化器就會選擇row行數(shù)比較少的索引。

3. 刪除無效的索引,或者新建更優(yōu)的索引。

?著作權(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ù)。

相關(guān)閱讀更多精彩內(nèi)容

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