1. 程式人生 > 其它 >日誌:MySQL 百萬級資料表索引失效解決方法

日誌:MySQL 百萬級資料表索引失效解決方法

技術標籤:mysqlsql

開門見山

今天偶然發現線上一SQL執行緩慢

druid監控頁面
druid 監控

最慢的時候達到了 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

未完待續……回家再寫