記一次order by 索引導致sql慢問題

 

1.故事背景

使用mysqldumpslow監控到一個列表慢sql,EXPLAIN sql 顯示Type爲index,key爲排序字段索引,看解釋結果應該不會慢。

2.數據交代

  • 表結構及全表數據(總數據32w)

CREATE TABLE `terminal` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `term_sn` varchar(36) NOT NULL,
  `modifydate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `term_sn` (`term_sn`),
  KEY `modifydate_id` (`modifydate`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

3.慢sql分析

  • sql執行時間(31s)

  • sql解釋 -因爲term_sn使用了 like %(%在左邊)  ,所以用不上term_sn索引,Mysql就會自動選擇order by字段的索引(modifydate_id索引)

而當我們強制不使用modifydate_id索引時,查詢速度爲1.4s,兩者查詢速度比爲31.4/1.4=22.4(倍)

解析不使用索引的sql

現象:使用filesort比不使用filesort 效率更高

4.原理分析

  • 使用order by 索引的sql分析

select * from terminal where term_sn like '%0820197670%' order by modifydate desc limit 15;

索引用於ORDER BY子句時,會直接遍歷該索引的葉子節點鏈表(B+樹索引詳情見文章底部)。執行流程如下:

1.從modifydate索引的第一個葉子節點出發,按順序掃描所有葉子節點獲取真實的行數據(具體操作見第二步)

2.根據每個葉子節點記錄的主鍵id去主鍵索引(聚簇索引))找到真實的行數據,判斷行數據是否滿足WHERE子句的term_sn條件,若滿足,則取出並返回

第一步依次順序獲取索引樹的葉子節點,這一步很快,但是第二步,由於rowid是根據modifydate進行排序的,第二次去查找真實的行數據,會按照rowid亂序去讀取行記錄,這些行數據在磁盤的存儲是分散的,每讀一行都會產生尋址時延(磁臂移動到指定磁道)+旋轉時延(磁盤旋轉到指定扇區),因此使用了order by 索引的情況下會很慢,這個本質就是隨機I/O。

注:並且此時查詢效率跟滿足WHERE子句的term_sn條件的數據多少/滿足條件數據的修改時間有關。

如果模糊搜索條件爲term_sn like '%0820%'(滿足該條件的數據特別多),上述sql查詢也會很快, 因爲limit和order by 子句或者group by子句聯合使用,mysql都對limit操作的查詢實行了懶惰策略,指要查詢的結果達到了length,就不再據需往下操作了,而我們上述的sql能查的數據小於15條,則會遍歷整個表。

如果我們需要搜索的term_sn like '%0820197670%' 滿足的這條記錄修改時間靠近當前時間,上述sql查詢也會變快,因爲我們使用modifydate倒序,能夠更快查到數據

  • 禁止order by 索引的sql

select * from terminal ignore index(modifydate_id) where term_sn like '%0820197670%'
order by modifydate desc limit 15;

禁止使用order by 索引的sql時,先掃表篩選出符合條件的數據,再將篩選結果根據modifydate排序 。執行流程如下

1.掃描全表篩選出滿足WHERE子句的term_sn條件的所有數據行,生成一張臨時表放入排序緩衝區

2.對臨時表緩衝區裏的數據進行排序

第一步雖然進行了全表掃描,但是這一步遍歷使用的是順序IO ,相對與上面的隨機IO會快很多。而且因爲過濾後的臨時表數據很少,使用filesort排序也會很快。

備註:

B+樹索引:InnoDB存儲引擎以B+樹作爲索引的底層實現,B+樹的葉子節點存儲着所有數據頁而內部節點不存放數據信息,並且所有葉子節點形成一個(雙向)鏈表
舉個例子,假設userinfo表的userid字段上有主鍵索引,且userid目前的範圍在1001~1006之間,則userid的索引B+樹如下:(這裏只是爲了舉例,下圖忽略了InnoDB數據頁默認大小16KB、雙向鏈表,並且假設B+樹度數爲3、userid順序插入)

 

5.解決方案

1.去除modifydate索引

2.根據是否存在搜索條件判斷是否禁止使用modifydate索引

 

參考資料

https://segmentfault.com/a/1190000015987895

 


 

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