慢SQL排查三板斧:SHOW PROCESSLIST + 慢查询日志 + EXPLAIN 实战
我是小耶,干运营半路出家的野生DBA——写功课只是为了我踩过的坑,你们别再踩了!
业务反馈“页面好卡”,登录数据库一看CPU 100%,但不知道是哪个SQL搞的鬼。这种场景,我每月至少遇到两次。今天就把我排查慢查询的三件套工具整理出来,全是实战经验。
第一板斧:SHOW PROCESSLIST —— 抓现行
当故障正在发生时,这是最快的定位手段。
SHOW PROCESSLIST;
输出结果中重点关注三列:
Command:值为Query表示正在执行的查询Time:执行了多少秒,数字越大越可疑,超过10秒的就要警惕Info:具体的SQL语句
实战场景:某天CPU无故飙升,执行 SHOW PROCESSLIST 发现大量 SELECT * FROM orders WHERE DATE(order_date)=...,Time 列普遍在20-30秒,说明有一批慢查询在同时跑。
拿到 Id 后,可以先 KILL <id> 杀死这些查询,让业务先恢复。然后再去分析为什么慢。
进阶用法:用 SHOW FULL PROCESSLIST 可以看到完整的SQL(不会截断)。也可以用以下查询直接获取需要关注的慢查询信息:
SELECT id, user, host, db, command, time, state, info
FROM information_schema.PROCESSLIST
WHERE command != 'Sleep'
AND time > 10 -- 超过10秒
ORDER BY time DESC;
注意:如果慢查询太多,频繁执行 SHOW PROCESSLIST 本身也有开销,建议在监控系统(如Prometheus)中定时采集。
第二板斧:慢查询日志 —— 事后复盘
很多慢查询不是实时发生的,而是间歇性出现。这时候就需要慢查询日志。
开启慢查询日志
在MySQL配置文件(my.cnf)中设置:
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
log_queries_not_using_indexes = ON # 额外记录不走索引的查询
long_query_time = 2:超过2秒的SQL会被记录(可根据业务调整,OLTP通常设1~2秒)log_queries_not_using_indexes:记录所有不走索引的查询,即使执行很快也记,便于发现隐患。
生效方式:重启MySQL或执行 SET GLOBAL slow_query_log=ON;(动态修改后立即生效,但重启会失效,最好同步修改配置文件)。
分析慢查询日志
最常用的是 mysqldumpslow 工具:
# 按查询时间排序,列出前10条
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# 按平均查询时间排序
mysqldumpslow -s at -t 10 /var/log/mysql/slow.log
# 按照某个模式过滤(例如只查看orders表的慢查询)
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log | grep orders
参数说明:
-s t:按总时间排序-s at:按平均时间排序-t 10:只显示前10条-g pattern:过滤条件
如果没有服务器登录权限,可以把slow.log拖到本地,用 pt-query-digest(Percona Toolkit)生成更详细的报告,它会按SQL指纹聚合,显示执行次数、平均时间、总时间等。
每天早晨固定动作:看昨天慢查询日志中最慢的几条SQL,提前优化,不要等用户投诉。
第三板斧:EXPLAIN —— 刨根问底
抓到慢SQL之后,为什么慢?用 EXPLAIN 看执行计划。
EXPLAIN SELECT * FROM orders WHERE order_date = '2026-05-01';
重点看这几列:
| 列名 | 怎么看 | 危险信号 |
|---|---|---|
type |
访问类型 | ALL(全表扫描)危险;ref/range 还行;const 完美 |
possible_keys |
可能用到的索引 | 如果为 NULL,说明没有可用的索引 |
key |
实际使用的索引 | NULL 意味着没走索引 |
rows |
预估扫描行数 | 数字越大越慢,超过表总行数的10%就要警惕 |
Extra |
附加信息 | Using filesort(额外排序)、Using temporary(临时表)需要优化 |
实战案例
一条慢查询:
SELECT * FROM orders WHERE DATE(order_date) = '2026-05-01';
执行 EXPLAIN 得到:
type=ALLrows=1000000Extra=Using where
问题:对索引列用了 DATE() 函数,导致全表扫描。改写为:
SELECT * FROM orders
WHERE order_date >= '2026-05-01' AND order_date < '2026-05-02';
再次 EXPLAIN:
type=rangerows=523Extra=Using index condition
快了几千倍。
EXPLAIN 高级用法
-
EXPLAIN FORMAT=JSON ...:得到JSON格式输出,包含成本估算(cost_info),用于更精细的分析。例如:"cost_info": { "read_cost": "1.00", "eval_cost": "0.20", "prefix_cost": "1.20", "data_read_per_join": "1K" }这里
read_cost和eval_cost可以帮助你判断优化器为什么选择某个方案。 -
EXPLAIN ANALYZE(MySQL 8.0.18+):真正执行查询并返回实际执行成本、返回行数、循环次数等,比普通EXPLAIN更准确。但注意——它会真实执行,生产环境慎用。
EXPLAIN ANALYZE SELECT * FROM orders WHERE order_date >= '2026-05-01';
输出中包含执行时间、实际扫描行数,是调优的利器。
三招联动流程图
用户反应慢
↓
SHOW PROCESSLIST 抓现场 → 如果有现成的慢查询,直接分析
↓ 没有现场或需要优化
慢查询日志 → 找出最多的慢SQL模式
↓
EXPLAIN 分析 → 发现索引问题、写SQL问题
↓
优化 → 加索引、改SQL
↓
验证 → 再次 EXPLAIN 或 压测
小技巧:写个Shell脚本每天凌晨将慢查询日志发到钉钉/企微,格式参考:
【慢查询日报】2026-05-08
最慢SQL:SELECT * FROM orders WHERE… 执行时间:12.3秒 出现次数:45
建议:给 order_date 增加索引
这样团队每个人都能看到,而不是只有你一个人扛着。
总结:一句话记住三板斧
抓现场用
SHOW PROCESSLIST,查历史用慢查询日志,分析原因用EXPLAIN。
三样工具用熟了,80%的慢查询你都能自己搞定。
小耶在手,SQL不愁。
你今天排查慢查询了吗?
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)