慢查询怎么排查?

#中间件#面试八股

慢查询怎么排查?

结论:慢查询排查分四步——先让慢 SQL「被记录下来」,再按指纹聚合出 TOP 消耗,然后对单条 SQL 看执行计划与真实耗时,最后才动手改 SQL 或索引。跳过第一步,所有优化都是猜。

一、先把慢日志打开

参数作用建议值
slow_query_log开关ON
slow_query_log_file日志路径独立磁盘目录
long_query_time阈值(秒,支持小数)1,排查期可降到 0.2
log_queries_not_using_indexes记录未走索引的语句排查期开,平时关(会刷爆日志)
min_examined_row_limit扫描行数低于该值不记录100,过滤噪声
log_slow_admin_statements是否记录 ALTER/OPTIMIZEON
log_slow_replica_statements从库是否记录按需
# my.cnf
[mysqld]
slow_query_log                = 1
slow_query_log_file           = /var/log/mysql/slow.log
long_query_time               = 1
min_examined_row_limit        = 100
log_slow_admin_statements     = 1
log_queries_not_using_indexes = 0
-- 运行期动态调整,无需重启
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.5;   -- 只对新连接生效
SHOW VARIABLES LIKE 'slow_query%';

二、排查漏斗

① 全局:哪类 SQL 最耗资源?
   performance_schema.events_statements_summary_by_digest
   pt-query-digest slow.log
        │
        ▼
② 单条:它的执行计划合理吗?
   EXPLAIN / EXPLAIN ANALYZE(8.0.18+)
   optimizer_trace
        │
        ▼
③ 现场:此刻谁在跑、卡在哪?
   SHOW PROCESSLIST / information_schema.processlist
   SHOW ENGINE INNODB STATUS(锁等待)
        │
        ▼
④ 定性:扫描行数、回表、排序、临时表、锁等待、网络

三、按指纹聚合

-- 按总耗时排序,找出最贵的 SQL 指纹
SELECT
  DIGEST_TEXT,
  COUNT_STAR                                AS exec_count,
  ROUND(SUM_TIMER_WAIT / 1e12, 2)           AS total_sec,
  ROUND(AVG_TIMER_WAIT / 1e9, 2)            AS avg_ms,
  SUM_ROWS_EXAMINED                         AS rows_examined,
  SUM_ROWS_SENT                             AS rows_sent,
  SUM_NO_INDEX_USED                         AS no_index
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 20;

rows_examined / rows_sent 的比值是最有用的信号:比值远大于 1,说明扫了大量行只返回几行,典型是索引不合适或条件不可索引。

# 离线分析慢日志,输出按总耗时排序的报表
pt-query-digest /var/log/mysql/slow.log > /tmp/slow_report.txt

四、单条 SQL 的深挖

-- 8.0.18+ 给出真实执行耗时与行数,而不是估算
EXPLAIN ANALYZE
SELECT id, title FROM article
WHERE author = 'ruilin' AND ctime > '2026-01-01'
ORDER BY ctime DESC
LIMIT 20;

-- 优化器为什么选了这个索引
SET optimizer_trace = 'enabled=on';
SELECT id FROM article WHERE author = 'ruilin' LIMIT 1;
SELECT TRACE FROM information_schema.optimizer_trace\G
SET optimizer_trace = 'enabled=off';

看 EXPLAIN 时重点盯四列:type(是否 ALL/index)、key(是否用了预期索引)、rows(估算扫描量)、Extra(Using filesort、Using temporary、Using index condition)。

五、常见结论与对应动作

  • type=ALL 且 rows 很大 → 缺索引或条件不可索引。
  • Using filesort → 排序字段不在索引顺序里,考虑联合索引覆盖排序。
  • Using temporary → GROUP BY/DISTINCT 走了临时表,缩小结果集或调整索引。
  • rows 很小但实际很慢 → 锁等待、回表随机 I/O、或结果集传输大。
  • 从库慢而主库不慢 → 检查 Seconds_Behind_Master、大事务、无主键表的 row 复制。

常见追问

  • 慢日志里为什么有执行很快的 SQL? 开了 log_queries_not_using_indexes,或者 long_query_time 设得极小;也可能是锁等待时间被计入。
  • long_query_time 改了为什么没生效? 它是会话级变量,已存在的连接仍用旧值,新连接才生效。
  • 没有慢日志权限怎么办? 用 performance_schema 的 digest 表,或 SHOW FULL PROCESSLIST 抓现场。
  • EXPLAIN 的 rows 为什么和实际差很多? 它是基于统计信息的估算,统计过期时执行 ANALYZE TABLE 刷新。
  • 慢查询优化后怎么验证? 对比优化前后的 rows_examined、Handler_read% 与 P99 耗时,并观察一段时间防止回归。

小结

慢查询治理是一条流水线:日志负责发现,digest 负责排序,EXPLAIN ANALYZE 负责定性,索引与 SQL 改写负责治疗。最容易被忽略的是最后一步的验证——没有前后指标对比的优化,等于没做。

笔记加载中…