MySQL 慢查询日志(Slow Query Log)完全梳理
慢查询日志是 MySQL 性能优化的核心入口之一。几乎所有线上问题、SQL 优化、索引缺失或超时排查,第一步通常都是查看慢查询日志。
下面从零到实战,把慢查询日志相关的核心知识点梳理清楚。
什么是慢查询日志?
简单来说,MySQL 会把执行时间超过某个阈值的 SQL 记录到日志文件里,这个阈值就是 long_query_time。
记录的内容通常包括:
- 执行时间
- 锁等待时间
- 返回的行数
- 扫描的行数
- 执行的完整 SQL
- 执行用户、IP、数据库名
- 是否使用了索引(using index / using filesort / using temporary)
核心参数
| 参数名 | 作用 | 默认值 | 推荐线上值 | 修改方式 |
|---|---|---|---|---|
| slow_query_log | 是否开启慢查询日志 | OFF | ON | 全局动态 |
| slow_query_log_file | 慢查询日志文件路径 | 主机名-slow.log | 自定义路径 | 全局动态 |
| long_query_time | 超过多少秒算慢查询 | 10 秒 | 0.5~2 秒(视业务) | 全局动态 |
| log_queries_not_using_indexes | 是否记录未使用索引的查询 | OFF | 建议开(排查利器) | 全局动态 |
| min_examined_row_limit | 扫描行数少于此值的不记录 | 0 | 一般不动 | 全局动态 |
| log_output | 日志输出方式(FILE / TABLE) | FILE | FILE 或 TABLE | 全局动态 |
生产环境推荐配置组合:
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 1; -- 1 秒以上算慢
SET GLOBAL log_queries_not_using_indexes = 1; -- 没走索引的都记
SET GLOBAL slow_query_log_file = '/data/mysql/slow/slow.log';
怎么查看慢查询日志?
方式一:直接看文件
tail -f /data/mysql/slow/slow.log
典型日志样子如下:
# Time: 2025-02-07T12:34:56.123456Z
# User@Host: app_user[app_user] @ [192.168.1.100] Id: 12345
# Query_time: 2.345 Lock_time: 0.000123 Rows_sent: 100 Rows_examined: 1000000
# Rows_affected: 0 Bytes_received: 0
SET timestamp=1738922096;
SELECT * FROM orders WHERE user_id = 123456 ORDER BY create_time DESC LIMIT 10;
关键字段解读:
Query_time:执行总耗时Lock_time:等待锁的时间Rows_examined:扫描了多少行(越大越危险)Rows_sent:返回给客户端多少行
方式二:写入 mysql.slow_log 表
SET GLOBAL log_output = 'TABLE';
然后就能用 SQL 查了:
SELECT sql_text, query_time, lock_time, rows_examined, rows_sent, user_host, db, last_seen
FROM mysql.slow_log
ORDER BY start_time DESC
LIMIT 100;
生产环境中如何高效分析慢查询?
常用流程:
- 打开慢查询 + 设置合理阈值(1s 或 0.5s)。
- 运行一段时间后,使用
mysqldumpslow工具汇总分析。
# 汇总显示前 20 条最慢的 SQL(按执行次数、耗时排序)
mysqldumpslow -s t -t 20 /data/mysql/slow/slow.log
mysqldumpslow -s c -t 20 /data/mysql/slow/slow.log # 按执行次数排序
- 用
pt-query-digest(Percona Toolkit)做更强大的分析。
pt-query-digest /data/mysql/slow/slow.log > digest.txt
它会给你:
- 总耗时占比
- 执行次数
- 平均/最大/最小耗时
- 95% 响应时间
- 指纹化的 SQL(自动参数化)
- 快速定位 SQL 问题:
Rows_examined很大 → 全表扫描、缺少索引Lock_time很高 → 行锁、表锁、事务长时间未提交Using filesort / Using temporary→ 排序或临时表开销大Query_time远大于Lock_time→ 可能是 IO 慢、数据量大
常见误区 & 注意事项
long_query_time = 0会记录所有查询(慎用,日志会爆炸)- 慢查询日志默认不记录
SELECT SLEEP(10)这种主动慢查询 log_queries_not_using_indexes = ON非常有用,但会增加少量开销- 慢查询日志是异步写的,日志里时间可能比实际执行晚一点
- 高并发下日志文件可能非常大,建议设置
expire_logs_days或定期清理 - 5.7 和 8.0 之后,慢查询日志对复制线程的 SQL 也可以记录(
log_slow_replica_statements)
终极建议
- 开发阶段:本地 / 测试环境把
long_query_time设成 0.1~0.5 秒,养成看慢查询的习惯 - 上线前:用
pt-query-digest过一遍主要接口的 SQL - 线上:每天定时分析慢查询(可以结合 ELK、Prometheus + Grafana 做监控)
- 最常见的优化方向(优先级):
- 加索引(最有效)
- 避免 select *
- 优化 order by / group by
- 拆分大事务
- 减少扫描行数
一句话总结:慢查询日志不是用来'看日志'的,而是用来'找到最该优化的 SQL'的。
