因为show processlist仅显示“正在执行”的语句,而真正拖垮数据库的往往是已执行完毕但单次耗时≥3秒的sql,它们不占连接却消耗cpu、锁表、全表扫描,只有慢查询日志能记录其精确耗时、锁等待、扫描行数等关键指标。

为什么 slow_query_log 不能只靠 SHOW PROCESSLIST 抓问题
因为 SHOW PROCESSLIST 只能看“正在跑”的语句,而真正拖垮数据库的,往往是那些执行完就消失、但单次耗时 3 秒以上的查询。它们不卡住连接,却持续吃 CPU、锁表、扫全表——这类 SQL 在慢查询日志里才留痕。
常见错误现象包括:监控显示 CPU 偶发尖刺,但 SHOW PROCESSLIST 里查不到长事务;应用报超时,但 DBA 登上去却“一切正常”。这时候没开 slow_query_log,等于闭眼修车。
- 它记录的是「已执行完成」的语句,含精确耗时、锁等待、扫描行数等关键指标
- 不像 general log 那样全量记录(性能损耗大),它按需过滤,可控性强
- 配合
log_queries_not_using_indexes=ON,还能揪出隐性低效语句(比如 WHERE 字段没索引但执行很快,逃过long_query_time却仍伤性能)
long_query_time 设成 0.1 为什么反而让磁盘爆满
这不是阈值太低的问题,而是 MySQL 版本兼容性陷阱:long_query_time 支持毫秒精度,仅限 5.7.21 及以上版本。旧版本(比如 5.6 或 5.7.20)设成 0.1,MySQL 会直接向下取整为 0 秒——结果是所有查询都被记入慢日志。
生产环境最常踩的坑:
- 在测试机用新版本调通了
long_query_time = 0.5,上线后发现日志暴涨,实际是线上 MySQL 是 5.7.19 - 误以为“越小越好”,没结合
min_examined_row_limit控制噪音,导致日志里全是SELECT COUNT(*) FROM user这类轻量但扫描行多的语句 -
long_query_time对事务内每条语句单独计时,不是整个事务耗时。一条BEGIN; UPDATE ...; SELECT ...; COMMIT;中只有 SELECT 超时,也只记这一条
slow_query_log_file 路径写错或权限不对,日志根本不会生成
很多人执行了 SET GLOBAL slow_query_log = ON,再查 show variables like '%slow_query_log%' 看到 Value 是 ON 就以为成了——其实只要 slow_query_log_file 没配对、路径不存在、MySQL 用户无写权限,日志就静默失败,连错误都不报。
必须同时满足三个条件:
-
slow_query_log设为ON或1 -
slow_query_log_file是绝对路径(如/var/log/mysql/mysql-slow.log),且目录存在 - 运行
mysqld的系统用户(通常是mysql)对该路径有写权限(chown mysql:mysql /var/log/mysql/)
注意:slow_query_log_file 在 my.cnf 里配置后,运行时用 SET GLOBAL 修改会报错 Variable 'slow_query_log_file' is a read-only variable,只能改配置文件并重启。
用 mysqldumpslow 快速定位 Top 耗时 SQL 的真实姿势
mysqldumpslow 不是拿来直接跑的,得先筛数据再排序。默认输出杂乱,真正有用的组合是:
-
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log:按总耗时排序,取前 10 条(-s t) -
mysqldumpslow -s r -t 5 /var/log/mysql/mysql-slow.log:按返回行数排序,揪出“查得多、返回少”的冤种查询(比如SELECT * FROM order WHERE status=0扫百万行只返回几十条) - 加
-g "JOIN"可过滤含 JOIN 的慢 SQL,避免被单表查询淹没
别忽略 Rows_examined 和 Rows_sent 的比值——如果前者是后者的上千倍,基本可以断定缺索引或写了 LIKE '%xxx' 这类无法走索引的条件。
复杂点在于:日志里语句是抽象归一化的(WHERE id = ?),看不出具体参数;而真正要优化的,往往就是某个特定 ID 导致回表爆炸。这时候得结合应用层日志或 general_log(临时开)交叉验证。











