跳转到内容

执行计划与慢查询

用途:把“这条 SQL 为什么慢”变成可验证的分析过程

  • slow_query_log=ON,long_query_time 建议 100-500ms(在线系统 1s 太宽),log_queries_not_using_indexes 谨慎开(噪声大)。
  • 分析工具:mysqldumpslow、Percona pt-query-digest、performance_schema / sys schema。
  • 关注总耗时占比而不是单条最慢:高频 50ms 的 SQL 往往比偶发 5s 的更影响整体。
列 看什么
type system/const/eq_ref 最佳;ref/range 可接受;index 全索引扫描;ALL 全表扫描
key / key_len 实际用到的索引与其前缀长度(判断联合索引用了几列)
rows 预估扫描行数(估算值,可能不准)
filtered 存储引擎返回的行中被 where 过滤后的百分比,低说明索引选择性差
Extra Using index(覆盖)、Using index condition(ICP)、Using filesort(额外排序)、Using temporary(临时表,需优化)

EXPLAIN ANALYZE(8.0.18+)会真实执行并给出每步实际耗时与行数,是判断优化器估算偏差的首选手段。

慢因 对策
索引缺失或选择性差 按查询重建复合索引,把等值列放前、区分度高的放前
函数/类型转换导致索引失效 改写为区间比较;必要时用函数索引或生成列
ORDER BY 触发 filesort 让排序列进入索引尾部且方向一致;或先取 ID 再回表
深分页 LIMIT 1000000,20 游标式分页(WHERE id > last_id ORDER BY id LIMIT 20)或延迟关联
大 IN 列表 分批、或临时表 join
统计信息陈旧导致选错索引 ANALYZE TABLE;必要时 FORCE INDEX 并跟进根因
优化器成本模型不适合 检查直方图(ANALYZE TABLE ... UPDATE HISTOGRAM)、考虑改写 SQL
锁等待(不是执行慢) 看 data_lock_waits,属于并发问题不是索引问题
缓冲池不足、页读盘 看命中率与 innodb_buffer_pool_size
  1. 确认慢在哪一层:执行时间 vs 锁等待 vs 网络/结果集大小。
  2. EXPLAIN + EXPLAIN ANALYZE 对照估算与实际。
  3. 改 SQL 或索引,小步验证(单条对比,不要一次改十处)。
  4. 压测或灰度观察 P95/P99,而不是只看平均。
  5. 上线后回到慢日志看板确认该模式消失。

SELECT * 的问题不只是“多几列”:破坏覆盖索引、增大网络与序列化开销、放大缓冲池占用。列表页明确列字段;导出类查询走游标分批或专用只读实例。

误区 修正
看 rows 小就认为快 锁等待、回表次数、排序都可能占大头
一律加索引 写放大与优化器选择成本;先看不走索引的原因
用 FORCE INDEX 当长期方案 数据分布变化后会劣化,需持续验证
只优化最慢的一条 高频中等耗时 SQL 才是主要 CPU 来源
生产直接跑大 EXPLAIN ANALYZE 它会真实执行,重 SQL 要在从库或低峰做
  • MySQL 官方文档:EXPLAIN、Optimizer Cost Model、Slow Query Log
  • Percona Toolkit 文档(pt-query-digest)
  • 《高性能 MySQL》第 6-8 章