【Java 开发日记】MySQL 慢查询日志(Slow Query Log)完全梳理
慢查询日志是 MySQL 性能优化的最重要入口之一,几乎所有线上问题、SQL 优化、索引缺失、超时排查,都会第一时间打开慢查询日志来看。
下面从零到实战,一次把慢查询日志相关的核心知识点全部讲清楚。
1. 什么是慢查询日志?
简单说:
MySQL 把执行时间超过某个阈值的 SQL 记录到日志文件里,这个阈值叫 long_query_time。
记录的内容通常包括:
- 执行时间
- 锁等待时间
- 返回的行数
- 扫描的行数
- 执行的完整 SQL
- 执行用户、IP、数据库名
- 是否使用了索引(using index / using filesort / using temporary)
2. 核心参数(必须记住的几个)
| 参数名 | 作用 | 默认值 | 推荐线上值 | 修改方式 |
|---|---|---|---|---|
| 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 = ON;
SET GLOBAL long_query_time = 1; -- 1秒以上算慢
SET GLOBAL log_queries_not_using_indexes = ON; -- 没走索引的都记
SET GLOBAL slow_query_log_file = '/data/mysql/slow/slow.log';
3. 怎么查看慢查询日志?
方式一:直接看文件(最原始)
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;
4. 生产环境中如何高效分析慢查询?
步骤推荐(最常用流程):
- 打开慢查询 + 设置合理阈值(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 慢、数据量大
5. 常见误区 & 注意事项
- 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)
6. 终极建议(Java 开发视角)
- 开发阶段:本地 / 测试环境把 long_query_time 设成 0.1~0.5 秒,养成看慢查询的习惯
- 上线前:用 pt-query-digest 过一遍主要接口的 SQL
- 线上:每天定时分析慢查询(可以结合 ELK、Prometheus + Grafana 做监控)
- 最常见的优化方向(优先级):
- 加索引(最有效)
- 避免 select *
- 优化 order by / group by
- 拆分大事务
- 减少扫描行数
一句话总结:
慢查询日志不是用来“看日志”的,而是用来“找到最该优化的 SQL”的。
有想深入的点吗?
比如:
- 怎么设置慢查询告警
- pt-query-digest 详细用法
- 8.0 慢查询日志的新特性
- 结合 MySQL Workbench / Navicat 看慢查询
欢迎继续聊~