应优先处理Lock_time高或Rows_examined远大于Rows_sent的SQL;Query_time仅作参考,真正瓶颈需结合Lock_time占比、扫描与返回行数比值及执行完成状态综合判断。

直接看 slow_query_log_file 里记录的 Query_time 最大的那几条,但别只盯这个值——Lock_time 高、Rows_examined 远大于 Rows_sent 的语句,才是真正该优先处理的慢 SQL。
怎么看慢查询日志文件里的关键字段
MySQL 5.7 的慢查询日志是纯文本格式,每条记录以 # Time: 开头,后面跟着执行时间、用户、主机、ID,再下一行就是核心统计行:Query_time: X.XXXXXX Lock_time: Y.YYYYYY Rows_sent: N Rows_examined: M。
重点关注三个组合:
-
Query_time高 +Lock_time占比 > 30% → 问题大概率在锁竞争,不是 SQL 本身写得差 -
Rows_examined是Rows_sent的几十倍甚至上千倍 → 很可能没走索引,或者索引失效(比如对字段用了函数、隐式类型转换) -
Rows_examined接近表总行数(比如查 10 万行表,Rows_examined是 98234)→ 基本确认是全表扫描,EXPLAIN里type字段会是ALL
用 mysqldumpslow 快速聚合高频/高耗时 SQL
手动翻日志效率低,MySQL 自带的 mysqldumpslow 工具能按模式归类。它不解析 SQL 语义,而是把变量值替换成 N 或 S 后合并统计。
常用命令:
-
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log:按Query_time总和排序,取前 10 条 -
mysqldumpslow -s c -t 5 /var/log/mysql/slow.log:按出现次数排序,找高频慢 SQL -
mysqldumpslow -g "SELECT.*orders" /var/log/mysql/slow.log:过滤含SELECT和orders的语句
注意:mysqldumpslow 默认把数字和字符串都替换成 N,所以 WHERE id = 123 和 WHERE id = 456 会被算作同一条——这正好帮你发现“参数化但没走索引”的通病。
为什么有些慢 SQL 在日志里找不到
MySQL 5.7 的慢查询日志只记录**已执行完成**的语句,且判断时机在语句返回之后。以下情况不会被记录:
- SQL 正在等锁(比如被
UPDATE持有行锁阻塞),还没开始执行或卡在等待阶段 - 客户端连接断开,SQL 被 MySQL 中断(
Killed状态),不会写入慢日志 -
long_query_time设为 1,但某条 SQL 执行了 0.9 秒 + 等待锁 2 秒,总耗时 2.9 秒,仍不会记录(因为执行阶段耗时未超阈值)
这种场景要配合 SHOW PROCESSLIST 或 information_schema.PROCESSLIST 查 State 字段为 Locked、Waiting for table metadata lock 的会话,再结合 INFORMATION_SCHEMA.INNODB_TRX 看谁在持有锁。
分析前先确认日志是否真在记录
很多人以为开了 slow_query_log = ON 就万事大吉,其实还有两个隐藏开关常被忽略:
-
log_output默认是FILE,但如果被设成TABLE,日志会写进mysql.slow_log表,而不是文件——此时slow_query_log_file设置无效 -
long_query_time是 SESSION 级变量,如果用SET long_query_time = 1(没加GLOBAL),只对当前会话生效,其他连接仍用默认 10 秒
务必用这条命令验证:SELECT @@global.slow_query_log, @@global.long_query_time, @@global.log_output;。生产环境建议统一用 GLOBAL 设置,并在 my.cnf 里固化。
真正难的不是找到哪条 SQL 慢,而是分清:它是执行慢(Query_time 主导)、等锁慢(Lock_time 主导)、还是扫得多返回少(Rows_examined / Rows_sent 比值失衡)。这三个方向对应完全不同的优化路径——索引、事务拆分、还是查询重写。


















