日誌:MySQL 百萬級資料表索引失效解決方法
阿新 • • 發佈:2020-12-20
開門見山
今天偶然發現線上一SQL執行緩慢
最慢的時候達到了 30.8 秒。
SQL程式碼段:
SELECT
id,
injection_machine_id,
value_code,
`value`,
sample_time
FROM
`表`
-- FORCE INDEX ( idx_machine_time )
WHERE
injection_machine_id = 26100
ORDER BY
sample_time DESC
LIMIT 1
經查,資料庫中存在完全符合的索引 idx_machine_time(索引順序為:injection_machine_id,sample_time),但使用的卻是 idx_time(單列 sample_time)。
注意:以下嘗試在你的機器上都可以嘗試一下,對你可能是有用的噢
嘗試1
於是我將該表匯出(包含結構和資料)放到了我本地測試伺服器,EXPLAIN 同樣的 SQL,並查看了二者的索引詳情後,發現二者的 Cardinality 有較大差別:
Cardinality 的值表示該索引列在整張表中唯一的值的個數,即:
select count(distinct `列`) from `表`
當存在多個可用索引時,且某索引的Cardinality 除以錶行數接近於1(或者說 Cardinality 接近錶行數)時,該索引有最大可能被命中,
但由於技術原因,在 innodb 引擎下的 Cardinality 值並不準確(在 MyISAM 引擎下準確)。
*Cardinality 本身的計算公式不在此詳述。
預設情況下,計算 Cardinality 的資料頁頁數有 20 頁,設定大一些讓 MySQL 取樣時可以更準確地計算 Cardinality,具體操作如下:
-- 先檢視 innodb_stats_persistent,看是 ON 還是 OFF show variables like 'innodb_stats_persistent' -- ON 的時候檢視這個,預設是20 show variables like 'innodb_stats_persistent_sample_pages' -- 設大一點 set global innodb_stats_persistent_sample_pages=40 -- OFF 的時候檢視這個,預設是8 show variables like 'innodb_stats_transient_sample_pages' -- 設大一點 set global innodb_stats_transient_sample_pages=16
接下來要執行以下 SQL,Cardinality 才會觸發重新計算:
-- innoDB
analyze table `表`
-- MyISAM
shell> myisamchk -a
innodb_stats_persistent_sample_pages /innodb_stats_transient_sample_pages 不宜設定太大,會導致自動或手動執行 analyze table 變慢
結果:
失敗了!
嘗試2
未完待續……回家再寫