【MySQL】全索引掃描的bug
一 簡介
在檢查某業務資料庫的slowlog 時發現一個慢查詢,查詢時間 1.57s ,檢查表結構 where條件欄位存在正確的組合索引,正確的情況下最佳化器應該選擇組合索引,而非為啥會導致慢查詢呢? 且看本文慢慢分析。
二 分析
案例中的MySQL資料庫版本 5.6.16 將生產環境的sql做適當修改,where條件不變。讀者朋友可以測試一下其他的版本。
分析
MySQL選擇的執行計劃利用主鍵進行訪問資料。注意執行計劃中的 access type是index,而index 意味著這個SQL在查詢二級索引的時候,對二級索引進行了全索引掃描,根本沒有進行過濾
這個行為是不合理的,因為where條件中含有 in 查詢,合理的執行計劃的access type應該是range。
我們採用強制索引,看看結果
分析
強制加上索引之後的執行計劃是符合預期的,執行sql的時間由 1.57s 減少為 0.01s 。因此我們推測是在最佳化器選擇索引的時候出現了問題
結合原始碼和optimize_trace我們發現第一階段最佳化的時候,最佳化器確實選擇了idx_sidustsvidtype 並且選擇採用range訪問,因為sql 語句中含有order by,在optimizer試圖最佳化 order by limit的時候
清空了儲存訪問方式的quick變數(原本儲存的是range,但是被請空),最終發現採用排序索引(這裡是id)的代價高於組合索引(這裡是idx_sidustsvidtype)時,還是選擇了idx_sidustsvidtype
但是悲劇的是這時候正確的訪問方式已經被清空,無法還原,這就是這個 的根本成因。
根據分析,我們還可以使用另一種解決方法----去掉 order by 。當然這個對業務所有入侵必須和開發溝通確認sql的結果集是否唯一,如果不唯一還是要使用其他方法。
三 總結
a 修改SQL,新增正確hint。
b 去掉不必要的order by 需要和開發溝通確認是否影響業務邏輯。
c 修改最佳化的bug,保留多個訪問路徑,不清理儲存訪問方式的quick變數,發現orderby 的代價高於組合索引時,可以選擇最優的訪問路徑。
特別感謝 的分析,同時也推薦《排序sql升級5.6變慢原因分析》
在檢查某業務資料庫的slowlog 時發現一個慢查詢,查詢時間 1.57s ,檢查表結構 where條件欄位存在正確的組合索引,正確的情況下最佳化器應該選擇組合索引,而非為啥會導致慢查詢呢? 且看本文慢慢分析。
二 分析
案例中的MySQL資料庫版本 5.6.16 將生產環境的sql做適當修改,where條件不變。讀者朋友可以測試一下其他的版本。
-
root@rac1 10:48:11>explain select id,
-
-> gmt_create,
-
-> gmt_modified,
-
-> order_id,
-
-> service_id,
-
-> seller_id,
-
-> seller_nick,
-
-> sale_type
-
-> from lol
-
-> where seller_id= 1501204
-
-> and service_id= 1
-
-> and sale_type in(3, 4)
-
-> and use_status in(3, 4, 5, 6)
-
-> and process_node_id= 6 order by id desc limit 0,20 \G
-
*************************** 1. row ***************************
-
id: 1
-
select_type: SIMPLE
-
table: lol
-
type: index
-
possible_keys: idx_sellerid,idx_usestatus_saletype,idx_sellerid_saletype,idx_sidustsvidtype
-
key: PRIMARY
-
key_len: 8
-
ref: NULL
-
rows: 3076
-
Extra: Using where
- 1 row in set (0.00 sec)
MySQL選擇的執行計劃利用主鍵進行訪問資料。注意執行計劃中的 access type是index,而index 意味著這個SQL在查詢二級索引的時候,對二級索引進行了全索引掃描,根本沒有進行過濾
這個行為是不合理的,因為where條件中含有 in 查詢,合理的執行計劃的access type應該是range。
我們採用強制索引,看看結果
-
root@rac1 10:48:07>explain select id,
-
-> gmt_create,
-
-> gmt_modified,
-
-> order_id,
-
-> service_id,
-
-> seller_id,
-
-> seller_nick,
-
-> sale_type
-
-> from lol force index(idx_sidustsvidtype)
-
-> where seller_id= 1501204
-
-> and service_id= 1
-
-> and sale_type in(3, 4)
-
-> and use_status in(3, 4, 5, 6)
-
-> and process_node_id= 6 order by id desc limit 0,20 \G
-
*************************** 1. row ***************************
-
id: 1
-
select_type: SIMPLE
-
table: lol
-
type: range
-
possible_keys: idx_sidustsvidtype
-
key: idx_sidustsvidtype
-
key_len: 19
-
ref: NULL
-
rows: 5178
-
Extra: Using where; Using filesort
- 1 row in set (0.00 sec)
強制加上索引之後的執行計劃是符合預期的,執行sql的時間由 1.57s 減少為 0.01s 。因此我們推測是在最佳化器選擇索引的時候出現了問題
結合原始碼和optimize_trace我們發現第一階段最佳化的時候,最佳化器確實選擇了idx_sidustsvidtype 並且選擇採用range訪問,因為sql 語句中含有order by,在optimizer試圖最佳化 order by limit的時候
清空了儲存訪問方式的quick變數(原本儲存的是range,但是被請空),最終發現採用排序索引(這裡是id)的代價高於組合索引(這裡是idx_sidustsvidtype)時,還是選擇了idx_sidustsvidtype
但是悲劇的是這時候正確的訪問方式已經被清空,無法還原,這就是這個 的根本成因。
根據分析,我們還可以使用另一種解決方法----去掉 order by 。當然這個對業務所有入侵必須和開發溝通確認sql的結果集是否唯一,如果不唯一還是要使用其他方法。
-
root@rac1 10:48:15>explain select id,
-
-> gmt_create,
-
-> gmt_modified,
-
-> order_id,
-
-> service_id,
-
-> seller_id,
-
-> seller_nick,
-
-> sale_type
-
-> from lol
-
-> where seller_id= 1501204
-
-> and service_id= 1
-
-> and sale_type in(3, 4)
-
-> and use_status in(3, 4, 5, 6)
-
-> and process_node_id= 6 \G
-
*************************** 1. row ***************************
-
id: 1
-
select_type: SIMPLE
-
table: lol
-
type: range
-
possible_keys: idx_sellerid,idx_uts_stp,idx_sid_stpe,idx_sidustsvidtype
-
key: idx_sidustsvidtype
-
key_len: 19
-
ref: NULL
-
rows: 5178
-
Extra: Using where
- 1 row in set (0.00 sec)
a 修改SQL,新增正確hint。
b 去掉不必要的order by 需要和開發溝通確認是否影響業務邏輯。
c 修改最佳化的bug,保留多個訪問路徑,不清理儲存訪問方式的quick變數,發現orderby 的代價高於組合索引時,可以選擇最優的訪問路徑。
特別感謝 的分析,同時也推薦《排序sql升級5.6變慢原因分析》
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22664653/viewspace-1823030/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL中的全表掃描和索引樹掃描MySql索引
- 索引全掃描和索引快速全掃描的區別索引
- mysql下建立索引讓其index全掃描MySql索引Index
- Oracle中存取資料掃描Table及索引的方式(全表掃描,索引掃描等)Oracle索引
- oracle 全表掃描,索引範圍掃描與塊的理解Oracle索引
- 使用索引快速全掃描(Index FFS)避免全表掃描的若干場景索引Index
- (轉)索引掃描還是全表掃描(Index Scan Or Full Table Scan)索引Index
- 轉)索引掃描還是全表掃描(Index Scan Or Full Table Scan)索引Index
- mysql索引覆蓋掃描優化MySql索引優化
- 有索引卻走全表掃描的實驗分析索引
- 【Oracle】 索引的掃描方式Oracle索引
- 全表掃描的cost 與 索引掃描Cost的比較 – 無直方圖(10.1.0.3以後)索引直方圖
- 解讀Oracle 索引掃描Oracle索引
- 走索引掃描的慢查詢索引
- 優化全表掃描優化
- delete 與全表掃描delete
- 查詢全表掃描的sqlSQL
- 索引掃描可能不如全表掃描的場景的理解__純粹資料量而言,不涉及CLUSTERING_FACTOR索引
- ORACLE全表掃描查詢Oracle
- 使用索引掃描來進行排序索引排序
- oracle實驗記錄(分割槽全表掃描(全區掃描) FTS 時候的成本計算)Oracle
- delete 刪除資料 全表掃描還是掃描所有塊的測試delete
- 查詢全表掃描語句
- oracle優化:避免全表掃描Oracle優化
- oracle是如何進行全表掃描的Oracle
- 優化Oracle with全表掃描的問題優化Oracle
- Oracle優化-索引原理[注意索引跳躍式掃描!Oracle優化索引
- PostgreSQL技術內幕(七)索引掃描SQL索引
- stopkey對索引掃描的影響測試TopK索引
- 執行計劃-資料訪問方式(全表掃描與4種索引的方式)索引
- @dbsnake-用合適的函式索引來避免看似無法避免的全表掃描函式索引
- 一條全表掃描sql語句的分析SQL
- 優化Oracle with全表掃描的問題(二)優化Oracle
- 抓取全表掃描的表,篩選和分析
- 索引唯一性掃描(INDEX UNIQUE SCAN)索引Index
- AppBoxFuture: 二級索引及索引掃描查詢資料APP索引
- 掃描技術和掃描工具
- noworkload下全表掃描cost的計算