慢查询日志本质:MySQL 把执行超过阈值、或未走索引的 SQL,按顺序记录到日志文件 /mysql.slow_log表里。 核心目标:找到热点 SQL、区分是真的执行慢,还是锁等待、扫描行数巨大。
前置:先确认配置
show variables like 'slow_query_log_file'; -- 日志文件路径
show variables like 'long_query_time'; -- 阈值(秒)
show variables like 'log_queries_not_using_indexes'; -- 记录无索引SQL
show variables like 'log_output'; -- FILE / TABLE
一、慢日志原始字段含义(文本格式)
一条典型慢日志片段示例:
# Time: 2026-10-28T14:20:01.123456+08:00
# User@Host: app[app] @ [127.0.0.1] Id: 12345
# Query_time: 2.123456 Lock_time: 0.000100 Rows_sent: 10 Rows_examined: 250000
SET timestamp=1761632401;
select * from t_order where user_id=10086;
重点 4 个指标(重中之重)
Query_time:SQL 从开始到结束总耗时(包含等待锁时间!!很多人踩坑)
Lock_time:等待行锁 / 表锁消耗的时间
Lock_time 高:不是 SQL 本身慢,被其他事务阻塞,优先查长事务、锁等待
Lock_time 很小,但 Query_time 很大:是 SQL 本身执行慢(扫描数据多、缺少索引、排序)
Rows_sent:返回给客户端行数
Rows_examined:MySQL扫描读取的行数 ✅ 核心判断:
Rows_examined >> Rows_sent,说明扫描大量无效数据,典型索引缺失、索引失效。
口诀: Lock_time 高 → 查锁、长事务 Lock_time 低,但 Query_time 高、Rows_examined 巨大 → 索引 / SQL 写法问题
其他字段:
User@Host:来源账号和 IP,定位哪个服务Id:线程 ID,对应 show processlist 的 IDSET timestamp:SQL 执行时间戳
二、工具 1:mysqldumpslow(官方自带,最常用)
作用:合并相同指纹 SQL、聚合统计,避免一条条看原始日志。 语法:
mysqldumpslow [参数] 慢日志文件路径
常用参数
-s:排序字段t:总耗时(sum Query_time,优先看这个,找最耗资源的热点)at:平均耗时c:执行次数(高频 SQL,即使单次不快,累计压力大)r:返回行数ar:平均返回行数l:锁时间
-t N:只输出 TOP N 条-g "正则":过滤 SQL,如-g "select"/-g "update"-a:不抽象数字、字符串(默认会把值替换成 S、N,看不到真实值)
常用命令模板
# 1. 按总耗时TOP10(优先)
mysqldumpslow -s t -t 10 /var/lib/mysql/localhost-slow.log
# 2. 按执行次数TOP20(高频小慢SQL,压垮DB)
mysqldumpslow -s c -t 20 /var/lib/mysql/localhost-slow.log
# 3. 只看select语句,平均耗时排序
mysqldumpslow -s at -g "select" -t 15 /var/lib/mysql/localhost-slow.log
# 4. 显示真实参数,不抽象
mysqldumpslow -s t -a -t 10 /var/lib/mysql/localhost-slow.log
输出解读示例:
Count: 80 Time=1.20s (96s) Lock=0.00s (0s) Rows=10.0 (800), app[app]@127.0.0.1
SELECT * FROM t_order WHERE user_id = N
Count:执行 80 次
Time:单次平均 1.2s,累计 96s
Lock:几乎无锁等待,确定是 SQL 本身慢
Rows:每次返回 10 行,总共返回 800 行,结合原始日志看 examined 行数
⚠️ 缺点:mysqldumpslow不展示 Rows_examined,想看扫描行数,要么看原始日志、要么用 pt-query-digest
三、工具 2:pt-query-digest(Percona Toolkit,生产首选,强烈推荐)
比 mysqldumpslow 强很多:自带 Rows_examined 统计、占比、直方图、输出报告,线上慢日志分析标准工具。
# 基础分析
pt-query-digest /var/lib/mysql/localhost-slow.log
# 只分析最近1小时的慢日志
pt-query-digest --since '1h ago' /var/lib/mysql/localhost-slow.log
# 过滤select
pt-query-digest --filter '$event->{fingerprint} =~ m/select/i' /var/lib/mysql/localhost-slow.log
报告 3 大块:
全局统计:总查询数、总耗时、95 分位耗时
热点 SQL 排名(按总耗时占比,重点看 %)
单条详情:包含 Query_time / Lock_time / Rows_examined / Rows_sent / 执行时间分布
重点看
Rows_examined,评估扫描效率。
四、工具 3:直接查表模式 log_output=TABLE
日志存在 mysql.slow_log,适合不方便拿日志文件、云数据库(很多云 DB 不给日志文件,只能查表)
SELECT
start_time,
query_time,
lock_time,
rows_sent,
rows_examined,
db,
user_host,
sql_text
FROM mysql.slow_log
WHERE start_time >= NOW() - INTERVAL 1 HOUR
ORDER BY query_time DESC
LIMIT 100;
可以自己写 SQL 做聚合(按 SQL 指纹分组统计)
五、标准分析步骤(固定流程,直接落地)
拿到慢日志后,按这个顺序排查,避免瞎优化索引
筛选时间段:优先故障时段日志
聚合统计:用 pt-query-digest /mysqldumpslow 抓 TOP 热点 SQL
区分两类慢 SQL:
低频、单次极慢(比如报表、大查询)
高频、单次几百 ms,执行几万次(最容易打垮数据库)
看 Lock_time
Lock_time 高:停止索引优化,转向排查长事务、锁等待、gap 锁
Lock_time 低:继续分析 SQL 本身
对比 Rows_examined 和 Rows_sent
examined ≈ sent ✅:效率不错,可能只是数据量大、排序分组
examined >> sent ❌:大概率索引缺失、索引失效、过滤条件写的不好
拿 SQL 执行 EXPLAIN 复制 SQL,explain 看执行计划:type、key、Extra(Using filesort/temporary)
验证优化效果:上线后持续观察该 SQL 在慢日志里是否消失
六、典型慢日志案例判断
案例 1
Query_time: 3.0 Lock_time: 2.8 Rows_sent: 1 Rows_examined:1
✅ 判断:锁等待,SQL 本身扫描 1 行很快,等锁等了 2.8s,去查 innodb_lock_waits、长事务。
案例 2
Query_time: 2.5 Lock_time:0.001 Rows_sent:5 Rows_examined:400000
✅ 判断:索引失效 / 缺少索引,扫描 40w 行只返回 5 条,优先加索引、改写条件。
案例 3
Query_time:1.8 Lock_time:0.002 Rows_sent:30000 Rows_examined:32000
✅ 判断:查询本身就要读出大量数据(导出、报表),不是索引问题,考虑异步、OLAP、分页、限制返回。
七、常见坑
long_query_time 是 SQL 执行完成后才记录:正在跑的 SQL 不会进慢日志,正在执行的 SQL 要用
show processlist/performance_schema 抓log_queries_not_using_indexes开启后,小表全扫描也会刷大量日志,淹没真正慢 SQL,线上不建议长期开启慢日志里的 Query_time 包含网络等待、锁等待,不等于 CPU 执行时间
mysqldumpslow 默认把参数抽象成 N / S,看不到真实值,排查问题时加
-a云 MySQL(阿里云 RDS、腾讯云 CDB)一般支持控制台直接看慢查询分析,底层也是这套逻辑
八、排查优先级总结
锁等待(Lock_time 高) > 扫描行数巨大(examined 超大) > 文件排序 / 临时表 > 高频小慢查询 > 离线报表类大查询
本文原创作者:易君召,详见:https://www.yijunzhao.cc/about,转载请注明出处。
原文链接
欢迎访问