高性能Mysql——Mysql通過基數判斷放棄索引而走全表掃描

文章目錄
引入
什麼時候走全表掃描
mysql如何預測判斷
引入
有時候我們使用explain來做分析的時候,有時候明明是設置了索引,但是也還是不走索引,而是走了全表掃描。是因爲Mysql做出了一個估計——走全表掃描的效率相比索引來說更高。

那麼Mysql判斷全表掃描更高效的依據有哪些呢?

我們假設數據庫表是這樣的:

CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  `d` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_c` (`c`) USING BTREE
) ENGINE=InnoDB;
1
2
3
4
5
6
7
我們在進行查詢操作的時候,例如

select * from t where 100 < c and c < 100000;
1
然而有些時候,就算你在 c 字段上有索引,系統也並不一定會走 c 這個字段上的索引,而是有可能會直接掃描掃描全表,找出所有符合 100 < c and c < 100000 的數據。

這是爲什麼呢?

什麼時候走全表掃描
我們知道,根據非聚集索引的原理,爲了查詢到某一個c的值,會先在非聚集索引上查找對應的主鍵,然後在聚集索引上查找對應的節點。所以,每個合適的c,都會在數據庫上走兩次索引。

而如果走全表掃描,每個c只會被查詢到一次,而像100<c<100000的這種情況,在c的總量n小於某個數值的時候,顯然是全表掃描比走兩次索引來的快。我們可以計算一下,如果走兩次索引,那麼如果c均勻分佈在100-100000的時候,那麼會走大約200000次索引掃描。只要全表的長度小於200000次,那麼全表掃描顯然是效率更高的。掃描行數越少,意味着I/O操作的次數越少。

mysql數據庫正是遵循了上面這個原理,他會估計哪種方式效率更高,IO次數更少,然後選擇使用合適的查找方式。不過,mysql是如何估計的呢?

之前我們的假設有一個前提,前提是c均勻分佈在某一個範圍內,而數據往往不是這麼均勻的,mysql也不可能像我們之前分析的那樣,有一個數學公式來判斷大於某個值就應該走索引之類的。

mysql如何預測判斷
系統是通過索引的區分度來判斷的,一個索引上不同的值越多,意味着出現相同數值的索引越少,意味着索引的區分度越高。我們也把區分度稱之爲基數,即區分度越高,基數越大。所以呢,基數越大,意味着符合 100 < c and c < 10000 這個條件的行數越少。基數越小,越意味着c的均勻範圍分佈。

所以呢,一個索引的基數越大,意味着走索引查詢越有優勢。

那麼問題來了,怎麼知道這個索引的基數呢?

系統當然是不會遍歷全部來獲得一個索引的基數的,代價太大了,索引系統是通過遍歷部分數據,也就是通過採樣的方式,來預測索引的基數的。

既然是基於統計的,那麼如果樣本有問題就可能導致結果有問題,也不能保證mysql是百分百確性走索引或者走全表掃描是效率最高的。也就是mysql也可能出現失誤的情況。例如你採樣的那一部分數據剛好基數很小,然後就誤以爲索引的基數很小。然後就呵呵,系統就不走 c 索引了,直接走全部掃描了。

所以呢,說了這麼多,得出結論:由於統計的失誤,導致系統沒有走索引,而是走了全表掃描,而這,也是導致我們 SQL 語句執行的很慢的原因。

不過呢,我們有時候也可以通過強制走索引的方式來查詢,例如:

select * from t force index(a) where c < 100 and c < 100000;
1
既然會預測錯索引的基數,這也意味着,當我們的查詢語句有多個索引的時候,系統有可能也會選錯索引哦,這也可能是 SQL 執行的很慢的一個原因。
————————————————
版權聲明:本文爲CSDN博主「No_Game_No_Life_」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/No_Game_No_Life_/article/details/106630197

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章