MySQL 慢查询诊断与 SQL 优化实战指南(适配 MySQL 8.4 LTS)
一、慢查询日志的核心作用
慢查询日志(Slow Query Log)是 MySQL 内置的第一道性能防线,用于自动捕获执行时间超过阈值的 SQL,为优化提供精准靶点。
- 定位瓶颈:找出拖慢应用的“罪魁祸首”SQL(如全表扫描、锁等待);
- 趋势分析:监控慢查询数量/耗时变化,评估系统健康度;
- 优化验证:改造后通过日志确认性能是否真正提升;
- 容量规划:高频慢查询暴露业务增长与架构瓶颈。
重要:慢日志是诊断工具,不是监控系统。应配合 Prometheus + Grafana 做长期趋势监控。
二、开启方式与关键配置(MySQL 8.4 LTS)
2.1 开启方式
动态开启(立即生效,重启失效)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log'; -- MySQL 8.4 支持动态改路径
永久开启(推荐)
[mysqld]
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1.0 # SRE 标准:1 秒(非默认 10 秒!)
log_queries_not_using_indexes = ON
min_examined_row_limit = 1000
log_slow_admin_statements = ON
log_slow_replica_statements = ON # MySQL 8.4 新增:记录从库慢查询
关键:MySQL 8.4 支持运行时动态修改
slow_query_log_file
,便于日志轮转。
2.2 关键参数详解与风险提示
参数 | 建议值 | 说明 | 风险提示 |
---|---|---|---|
long_query_time | 1.0 | SRE 标准阈值。默认 10 秒太迟钝,1 秒可捕获多数性能问题 | 过低(如 0.1)会产生海量日志 |
log_queries_not_using_indexes | ON | 辅助发现潜在全表扫描 | 会记录所有未使用索引的查询,包括无害的小表扫描和高危的大表全扫。必须配合 min_examined_row_limit 过滤,否则日志量可能爆炸。 |
min_examined_row_limit | 1000 | 仅记录扫描行数 ≥1000 的查询 | 避免因锁等待变慢但实际扫描少的“假慢查询” |
log_slow_admin_statements | ON | 记录慢 DDL(如 ALTER TABLE ) | 大表 DDL 必须监控 |
log_slow_replica_statements | ON | MySQL 8.4 新增:记录从库重放慢 SQL | 主从延迟排查利器 |
磁盘安全红线:
- 慢日志必须配置 logrotate(每日轮转 + 压缩);
- 禁止将日志写入系统盘(如
/var/log
未独立挂载);- 可通过
SET GLOBAL slow_query_log = OFF
紧急关闭。
三、慢查询分析:从日志到根因
3.1 分析工具对比
工具 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
mysqldumpslow | MySQL 自带,轻量 | 功能简陋,无法识别绑定变量 | 快速查看 Top N |
pt-query-digest | 行业标准,支持指纹归并、95分位、索引建议 | 需安装 Percona Toolkit | 深度分析、生成报告 |
Performance Schema(MySQL 8.0+) | 无 I/O 开销,实时查询,支持 EXPLAIN ANALYZE | 内存占用高,记录数有限(默认仅数千条) | 实时诊断利器 |
MySQL 8.4 最佳实践:
- 实时监控与快速诊断:优先使用 Performance Schema(低 I/O 开销,支持
EXPLAIN ANALYZE
);- 长期归档、审计、深度分析:仍需依赖 慢查询日志 + pt-query-digest。
理由:Performance Schema 的
events_statements_history_long
表是内存环形缓冲区,默认仅保留少量记录,无法替代慢日志的持久化与审计能力。
3.2 关键指标解读(pt-query-digest 输出)
重点关注:
- 95% 响应时间:比平均值更能反映用户体验;
- Rows_examined / Rows_sent:
- 比值 > 100 → 极可能缺少索引;
- 比值 ≈ 1 → 索引高效;
- Lock_time:高锁等待 → 检查行锁冲突或长事务;
- Full_scan:标记是否发生全表扫描。
实战案例:
Rows_examined: 1,200,000
,Rows_sent: 5
→ 立即检查 WHERE 条件列是否有索引。
四、SQL 优化手段(结合 MySQL 8.4 新能力)
4.1 索引优化(最高效手段)
- 为高频 WHERE 列建索引(选择性高者优先);
- 组合索引遵循最左前缀 + 覆盖原则;
-- 覆盖索引示例:避免回表 CREATE INDEX idx_cover ON orders (user_id, status, amount); SELECT status, amount FROM orders WHERE user_id = 123;
- MySQL 8.0+ 函数索引:解决
WHERE YEAR(create_time) = 2023
CREATE INDEX idx_func ON orders ((YEAR(create_time)));
4.2 SQL 改写(规避优化器陷阱)
- 避免
SELECT *
→ 只查必要字段; - 避免字段计算:
WHERE create_time + INTERVAL 1 DAY > NOW()
→ 改写为范围:WHERE create_time > NOW() - INTERVAL 1 DAY
; - 慎用
OR
:WHERE a=1 OR b=2
(若 b 无索引则全表扫描)
→ 改用 UNION 或确保两边都有索引; - 避免子查询(尤其相关子查询)
→ 优先用 JOIN(MySQL 8.0+ 对 JOIN 优化更强)。
4.3 执行计划验证:EXPLAIN ANALYZE(MySQL 8.0+)
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123;
- 返回实际执行时间、循环次数、是否物化等真实数据;
- 比传统 EXPLAIN 更可靠,可验证索引是否真正生效。
4.4 架构级优化
- 分页优化:
-- 避免:LIMIT 1000000, 10(深度分页) -- 改用游标分页: SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 10;
- 读写分离 + 查询路由:
- 将报表、分析类慢查询强制路由到只读副本;
- 使用 ProxySQL / ShardingSphere 实现智能路由。
- 冷热分离:
- 将 6 个月前订单归档至
orders_history
表; - 主表体积缩小 → 索引更小 → 缓存命中率更高。
- 将 6 个月前订单归档至
五、SRE 黄金法则
- 优化前先测量:通过慢日志或 Performance Schema 获取基线;
- 优化后必验证:对比 95% 响应时间、CPU、I/O 变化;
- 变更需灰度:索引/SQL 上线先在从库验证;
- 日志要治理:慢日志 ≠ 全量日志,合理设置阈值与过滤;
- 工具要分层:
- 实时诊断用 Performance Schema;
- 长期归档靠慢日志。
终极建议:
在 MySQL 8.4 LTS 时代,慢查询日志仍是生产环境的必备兜底手段,不可关闭。
同时应结合 Performance Schema + 自动化分析平台,构建“实时 + 历史”双轨诊断体系,实现可观测、可追溯、可治理的数据库性能管理。
本文档适用于 MySQL 5.7 / 8.0 / 8.4 生产环境,所有建议均来自大规模 OLTP 系统实战经验。
记住:工具无好坏,用错即灾难——这才是 SRE 的数据库治理之道。