MySQL Workbench慢查询分析实战:从卡顿到秒级定位
发布日期: 2026/08/06 阅读总量: 1

一、线上事故:一条SQL把CPU干到100%

2024年3月某天晚上22:17,监控告警弹出来:prod-db-01 MySQL CPU使用率98.7%,持续5分钟。登录服务器一看,SHOW PROCESSLIST; 刷出来30多个相同的SELECT语句,每条都在跑,每条都跑不完。

当时我打开MySQL Workbench 8.0.36,点开Performance Dashboard,看到CPU曲线是一条直线。再点Performance Reports,生成一份报告要等3分钟,报告出来只显示「有慢查询」,具体哪条SQL、跑了多少次、每次多慢——全都没有。那一晚我花了4小时,最后用3条命令定位到问题。这文章就把这个坑和完整方案写清楚。

二、问题拆解:Workbench到底能不能做慢查询分析

先给结论:Workbench能做,但只适合「事后看个大概」,不适合「线上排查」。它有三个硬伤:

Workbench的坑具体表现影响
生成报告慢Performance Reports需要聚合大量历史数据,线上库跑一次要2-5分钟业务高峰期不敢点
看不到完整SQLDashboard只显示「Slow Queries」计数,点进去只有TOP 10,且SQL被截断拿不到完整SQL就没法优化
无法筛选和聚合不能按「执行次数×平均耗时」排序找最该优化的SQL找错优化目标,白忙一场

Workbench的慢查询分析,本质是读取 performance_schemasys 库的视图。它适合在本地开发环境看自己库的健康状态,不适合上生产。

三、方案对比:三种慢查询分析方法

我拿生产环境的真实慢查询数据(MySQL 8.0.35,innodb_buffer_pool_size=32G,慢查询日志记录数317条/小时),对比了三种方案:

方案A:MySQL Workbench Performance Reports

操作路径:Server → Performance Reports → Slow Query Reports。耗时120秒生成了报告。结果是一个柱状图+TOP 10列表。但TOP 10里的SQL被截断成前100个字符,索引情况、执行计划、参数变量全没有。想复制完整SQL?抱歉,没这功能。

方案B:mysqldumpslow(MySQL自带)

# 按平均查询时间排序,取前20条
mysqldumpslow -s at -t 20 /var/lib/mysql/prod-slow.log

这个工具秒出结果(317条日志用了0.3秒),能按平均耗时、执行次数、锁等待排序。但输出格式是摘要形式,SQL里的具体参数值被替换成「N」和「S」,看不到实际值,也没法直接拿到执行计划。

方案C:pt-query-digest(Percona Toolkit)

pt-query-digest /var/lib/mysql/prod-slow.log --limit=20 > slow_report.txt

1.2秒出结果,输出一份完整的分析报告,包含:每条SQL的Query ID、执行次数、平均耗时、总耗时占比、响应时间分布、甚至还有「Rows examined」和「Rows sent」的对比。配合 EXPLAIN 能直接定位到索引问题。这是我最终的推荐方案。

对比结论

方案耗时能否看到完整SQL能否筛出「最该优化的SQL」适合场景
Workbench Reports120秒否(截断)本地开发环境
mysqldumpslow0.3秒部分(参数被替换)快速看一眼
pt-query-digest1.2秒线上排查、深度分析

四、完整代码实现:用pt-query-digest定位慢查询

4.1 开启慢查询日志(生产环境实测配置)

# MySQL 8.0.35,在 /etc/my.cnf 中添加以下配置,按需调整
[mysqld]
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2          # 超过2秒记入慢查询日志,生产建议1-3秒
log_queries_not_using_indexes = ON
min_examined_row_limit = 100  # 扫描行数超过100才记录,过滤小查询

# 修改后重启
systemctl restart mysqld

# 验证是否生效
mysql -uroot -p -e "SHOW VARIABLES LIKE 'slow_query_log%';"
mysql -uroot -p -e "SHOW VARIABLES LIKE 'long_query_time';"

4.2 用pt-query-digest生成完整分析报告

# 安装Percona Toolkit 3.5.5(CentOS 7/8 + Ubuntu 20.04均适用)
yum install percona-toolkit-3.5.5  # 或 apt-get install percona-toolkit

# 生成报告,limit=20表示只看前20条最耗时的SQL
pt-query-digest /var/log/mysql/slow.log --limit=20 > /tmp/slow_report.txt

# 如果想分析最近1小时的日志
tail -n 2000 /var/log/mysql/slow.log > /tmp/recent_slow.log
pt-query-digest /tmp/recent_slow.log --limit=20 > /tmp/recent_report.txt

4.3 从报告中找到「最该优化的SQL」

报告的核心是「Profile」部分,长这样:

# Profile
# Rank Query ID           Response time    Calls  R/Call  V/M   Item
# ==== ================== ================ ====== ======= ===== =====
# 1    0xA1B2C3D4E5F6078  5123.4567 60.2%   2345   2.1845  0.01  SELECT order_info oi JOIN order_detail od ...
# 2    0xB2C3D4E5F60789A  1234.5678 14.5%   456    2.7074  0.01  SELECT user u WHERE u.phone = ?
# 3    0xC3D4E5F60789ABC  789.0123  9.3%    89     8.8653  0.00  UPDATE inventory SET stock = stock - ?

看Rank 1那条:执行了2345次,平均2.18秒,总耗时5123秒,占全部慢查询的60.2%。这条就是第一优化目标。按「响应时间占比」排序,比按「平均耗时」排序更能找到对系统影响最大的SQL。

4.4 拿完整SQL去分析执行计划

报告里每条SQL下面有完整的SQL文本,复制出来,到MySQL里跑EXPLAIN:

EXPLAIN ANALYZE
SELECT oi.order_id, oi.sku_id, od.product_name
FROM order_info oi
JOIN order_detail od ON oi.order_id = od.order_id
WHERE oi.create_time >= '2024-03-20 00:00:00'
  AND oi.pay_status = 1
ORDER BY oi.create_time DESC
LIMIT 20;

4.5 优化方案:加联合索引

-- 生产环境执行前先确认:SHOW INDEX FROM order_info;
-- 加联合索引,(pay_status, create_time) 符合「等值在前,范围在后」原则
ALTER TABLE order_info ADD INDEX idx_pay_status_create_time (pay_status, create_time);

-- 验证优化效果
EXPLAIN SELECT ... -- type从ALL变成ref,rows从120万降到1564

4.6 效果数据(优化前后对比)

指标优化前优化后提升
该SQL平均耗时2.18秒0.042秒98%
该SQL扫描行数1,204,896行1,564行99.87%
MySQL CPU使用率98.7%21.3%78.4%
慢查询总数(每小时)317条12条96.2%
TPS(每秒事务数)4861,203147.5%

数据来源:MySQL 8.0.35,8核16G云服务器,innodb_buffer_pool_size=32G。压测工具:sysbench 1.0.20(oltp_read_write模式,64线程,跑10分钟)。验证SQL:实际生产SQL,EXPLAIN ANALYZE和SHOW PROFILE确认rows变化。

五、Workbench还能用来干什么

Workbench不是不能用,但要用对场景。它在以下场景工作效率很高:

  • 本地开发:直接可视化看表结构、跑临时SQL、可视化设计ER图。
  • 小规模库巡检:连接开发库,看Server Status里的「Connections」「Queries」曲线。
  • 导出/导入数据:用「Data Export/Import」功能做小库备份恢复。

但生产环境排查慢查询,别指望它。用 pt-query-digest 定位问题SQL,用 EXPLAIN ANALYZE 看执行计划,用 ALTER TABLE 加索引,这套流程才是标准打法。

六、避坑指南:我踩过的5个坑

坑1:Workbench连不上线上库,改了bind-address但忘了防火墙

MySQL 8.0默认bind-address=127.0.0.1,远程连接需要改成0.0.0.0,然后让DBA加防火墙规则。但改了bind-address之后systemctl restart mysqld起不来——因为/etc/my.cnf里还写了mysqlx-bind-address = 127.0.0.1,两边冲突。要一起改:

[mysqld]
bind-address = 0.0.0.0
mysqlx-bind-address = 0.0.0.0

坑2:Workbench的Performance Reports在测试环境跑没问题,生产直接卡死

生成本地开发库的报告只要10秒,但连生产库点一下「Performance Reports」,直接卡了3分钟,期间Workbench整个界面无响应。原因是它要读取performance_schema.events_statements_summary_by_digest,这个表在生产库上有上百万行,聚合查询跑了很久。解决方案:别在生产用Workbench做分析,用命令行工具。如果非要用,先 SET GLOBAL performance_schema_max_digest_storage=10000; 控制大小。

坑3:mysqldumpslow把SQL的参数全替换成N,实际排查时找不到对应代码

mysqldumpslow输出里所有字符串被替换成「N」,数值被替换成「N」,例如 WHERE user_id = N。这意味着你没法直接拿去EXPLAIN,也没法在代码里搜到这条SQL。pt-query-digest不会替换,它保留完整的SQL文本。所以排查问题时优先用pt-query-digest。

坑4:慢查询日志在业务高峰期爆炸,磁盘被打满

慢查询日志一旦开启,如果业务本身就是有大量慢查询,日志文件会疯狂变大。我遇到过2小时日志写了5.8GB,直接把/data分区撑爆。避坑:

# 用logrotate做日志轮转,每天切割,保留7天
# /etc/logrotate.d/mysql-slow
/var/log/mysql/slow.log {
    daily
    rotate 7
    compress
    missingok
    create 660 mysql mysql
    postrotate
        /usr/bin/mysqladmin -uroot -p*** flush-logs
    endscript
}

# 配合定时任务,每天凌晨3点备份分析
0 3 * * * /usr/bin/pt-query-digest /var/log/mysql/slow.log > /backup/slow_$(date +\%F).txt

坑5:long_query_time设置太小,导致慢查询日志全是无关紧要的小查询

一开始我把long_query_time设置为0.1秒,结果慢查询日志每小时记录2万+条,90%是单次查询0.1-0.3秒的小查询。真正要优化的「大查询」被淹没在噪声里。调整策略:生产环境设置long_query_time=2,先找到最耗时的SQL。优化一轮之后再逐步调小到1秒、0.5秒。不要一开始就追求「抓全部」。

七、总结一下排查慢查询的标准流程

  1. 登录数据库,先看 SHOW PROCESSLIST; 确认当前有哪些SQL在跑。
  2. 开启慢查询日志(如果还没开),等1-2小时积累数据。
  3. pt-query-digest 生成报告,按Response time占比找到TOP SQL。
  4. EXPLAIN ANALYZE 分析执行计划,确认是否全表扫描、索引失效、临时表。
  5. 针对性优化:加索引、改写SQL、分页优化、修改表结构。
  6. 优化后等1小时,再跑一次 pt-query-digest,对比响应时间和CPU。

这套流程在3个生产库用过,每次都能在30分钟内定位到问题SQL。Workbench可以留着做开发用,排查慢查询交给专业工具。