我是小耶,干运营半路出家的野生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=ALL
  • rows=1000000
  • Extra=Using where

问题:对索引列用了 DATE() 函数,导致全表扫描。改写为:

SELECT * FROM orders 
WHERE order_date >= '2026-05-01' AND order_date < '2026-05-02';

再次 EXPLAIN

  • type=range
  • rows=523
  • Extra=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_costeval_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不愁。

你今天排查慢查询了吗?

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐