mysql优化说出九条_技术分享 | MySQL 优化:为什么 SQL 走索引还那么慢?
原標(biāo)題:技術(shù)分享 | MySQL 優(yōu)化:為什么 SQL 走索引還那么慢?
背景
2019-01-11 9:00-10:00 一個 MySQL 數(shù)據(jù)庫把 CPU 打滿了。
硬件配置:256G 內(nèi)存,48 core
分析過程
接手這個問題時現(xiàn)場已經(jīng)不在了,信息有限,所以我們先從監(jiān)控系統(tǒng)中查看一下當(dāng)時的狀態(tài)。從 PMM 監(jiān)控來看,這個 MySQL 實例每天上午九點 CPU 都會升高到 10%-20%,只有 1 月 2 號 和 1 月 11 號 CPU 達(dá)到 100%,也就是今天的故障。懷疑是業(yè)務(wù)在九點會有壓力下發(fā),排查方向是慢查詢。
1. 按執(zhí)行次數(shù)統(tǒng)計 slow log 發(fā)現(xiàn)次數(shù)最多的一條 sql:
mysqldumpslow -s c slow.log>/tmp/slow_report.txt
Count: 3276 Time=21.75s (71261s) Lock=0.00s (1s) Rows=0.9 (2785), xxx
SELECT T.TASK_ID,
T.xx,
T.xx,
...
FROM T_xx_TASK T
WHERE N=N
AND T.STATUS IN (N,N,N)
AND IFNULL(T.MAX_OPEN_TIMES,N) > IFNULL(T.OPEN_TIMES,N)
AND (T.CLOSE_DATE IS NULL OR T.CLOSE_DATE >= SUBDATE(NOW,INTERVAL 'S' MINUTE))
AND T.REL_DEVTYPE = N
AND T.REL_DEVID = N
AND T.TASK_DATE >= 'S'
AND T.TASK_DATE <= 'S'
ORDER BY TASK_ID DESC
LIMIT N,N
2. 在 slow log 中找到這條查詢記錄掃描行數(shù):“Rows_examined: 1161559”,看起來是全表掃描,CPU 升高通常原因就是同時執(zhí)行大量慢 sql,所以接下來分析這個 sql
3. 因為 T_ xxx_TASK 表在現(xiàn)場應(yīng)急時清理過數(shù)據(jù)(從 110 萬刪至 4 萬行),所以需要用備份恢復(fù)該表到故障前。恢復(fù)備份后,查看執(zhí)行計劃與執(zhí)行時間:
explain SELECT T.TASK_ID,
T.xx,
...
FROM T_xxx_TASK T
WHERE 1=1
AND T.STATUS IN (1,2,3)
AND IFNULL(T.MAX_OPEN_TIMES,0) > IFNULL(T.OPEN_TIMES,0)
AND (T.CLOSE_DATE IS NULL OR T.CLOSE_DATE >= SUBDATE(NOW,INTERVAL '10' MINUTE))
AND T.REL_DEVTYPE = 1
AND T.REL_DEVID = 000000025xxx
AND T.TASK_DATE >= '2019-01-11'
AND T.TASK_DATE <= '2019-01-11'
ORDER BY TASK_ID DESC
LIMIT 0,20;
執(zhí)行時間 10s+:
1 row in set (10.37 sec)
表索引信息:
show index fromT_xxx_TASK;
看到這里其實已經(jīng)可以基本確定是這個 SQL 引起的了,因為執(zhí)行一次就要 10s+,而且那個時間點會并發(fā)下發(fā)大量的這個 SQL。但是有一點陷阱藏在這里:
1. 執(zhí)行計劃中明明有使用到索引,為什么執(zhí)行還是這么慢?
2. 執(zhí)行計劃中顯示掃描行數(shù)為 644,為什么 slow log 中顯示 100 多萬行?
a. 我們先看執(zhí)行計劃,選擇的索引 “INDX_ BIOM_ELOCK_ TASK3(TASK_ID)”。結(jié)合 sql 來看,因為有 "ORDER BY TASK_ ID DESC" 子句,排序通常很慢,如果使用了文件排序性能會更差,優(yōu)化器選擇這個索引避免了排序。
那為什么不選 possible_keys:INDX_ BIOM_ELOCK_ TASK 呢?原因也很簡單,TASK_DATE 字段區(qū)分度太低了,走這個索引需要掃描的行數(shù)很大,而且還要進(jìn)行額外的排序,優(yōu)化器綜合判斷代價更大,所以就不選這個索引了。不過如果我們強(qiáng)制選擇這個索引(用 force index 語法),會看到 SQL 執(zhí)行速度更快少于 10s,那是因為優(yōu)化器基于代價的原則并不等價于執(zhí)行速度的快慢;
b. 再看執(zhí)行計劃中的 type:index,"index" 代表 “全索引掃描”,其實和全表掃描差不多,只是掃描的時候是按照索引次序進(jìn)行而不是行,主要優(yōu)點就是避免了排序,但是開銷仍然非常大。
Extra:Using where 也意味著掃描完索引后還需要回表進(jìn)行篩選。一般來說,得保證 type 至少達(dá)到 range 級別,最好能達(dá)到 ref。
在第 2 點中提到的“慢日志記錄Rows_examined: 1161559,看起來是全表掃描”,這里更正為“全索引掃描”,掃描行數(shù)確實等于表的行數(shù);
c. 關(guān)于執(zhí)行計劃中:“rows:644”,其實這個只是估算值,并不準(zhǔn)確,我們分析慢 SQL 時判斷準(zhǔn)確的掃描行數(shù)應(yīng)該以 slow log 中的 Rows_examined 為準(zhǔn)。
4. 優(yōu)化建議:添加組合索引 IDX_ REL_DEVID_ TASK_ID(REL_ DEVID,TASK_ID)
優(yōu)化過程:
TASK_DATE 字段存在索引,但是選擇度很低,優(yōu)化器不會走這個索引,建議后續(xù)可以刪除這個索引:
select count(*),count(distinct TASK_DATE) from T_BIOMA_ELOCK_TASK;
+------------+---------------------------+
| count(*) | count(distinct TASK_DATE) |
+------------+---------------------------+
+------------+---------------------------+
在這個 sql 中 REL_DEVID 字段從命名上看選擇度較高,通過下面 sql 來檢驗確實如此:
select count(*),count(distinct REL_DEVID) from T_BIOMA_ELOCK_TASK;
+----------+---------------------------+
| count(*) | count(distinct REL_DEVID) |
+----------+---------------------------+
+----------+---------------------------+
由于有排序,所以得把 task_ id 也加入到新建的索引中,REL_DEVID,task_id 組合選擇度 100%:
select count(*),count(distinct REL_DEVID,task_id) from T_BIOMA_ELOCK_TASK;
+----------+-----------------------------------+
| count(*) | count(distinct REL_DEVID,task_id) |
+----------+-----------------------------------+
+----------+-----------------------------------+
在測試環(huán)境添加 REL_ DEVID,TASK_ID 組合索引,測試 sql 性能:alter table T_BIOMA_ELOCK_TASK add index idx_REL_DEVID_TASK_ID(REL_DEVID,TASK_ID);
添加索引后執(zhí)行計劃:
這里還要注意一點“隱式轉(zhuǎn)換”:REL_ DEVID 字段數(shù)據(jù)類型為 varchar,需要在 sql 中加引號:AND T.REL_DEVID = 000000025xxx >> AND T.REL_DEVID = '000000025xxx'
執(zhí)行時間從 10s+ 降到 毫秒級別:
1 row in set (0.00 sec)
結(jié)論
一個典型的 order by 查詢的優(yōu)化,添加更合適的索引可以避免性能問題:執(zhí)行計劃使用索引并不意味著就能執(zhí)行快。返回搜狐,查看更多
責(zé)任編輯:
總結(jié)
以上是生活随笔為你收集整理的mysql优化说出九条_技术分享 | MySQL 优化:为什么 SQL 走索引还那么慢?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sql跨数据库如何访问linux 下my
- 下一篇: qt调用mysql加密接口_Qt实现客户