← 中文资源

简体中文版本

对比加索引前后 MySQL 的查询计划

用 EXPLAIN ANALYZE 度量每步真实行数与耗时,证明索引真正改变了执行计划而不是凭感觉。

阅读英文原文

目标

普通 EXPLAIN 只展示优化器打算做什么,其行数估计常常错一个数量级;EXPLAIN ANALYZE 会真正执行查询并汇报每一步实际发生了什么,是证明“索引确实有效”的唯一诚实手段。本食谱用前后对比的方式度量真实行数与耗时,让加索引这件事从占卜变成实验,并把写入代价也算进账本。

步骤要点

  1. 先用 EXPLAIN ANALYZE 抓基线计划,读出每步的 actual time 与 actual rows,确认 orders 表全表扫描读了两百多万行。
  2. 用 EXPLAIN FORMAT=JSON 对比估计行数与真实行数的差距,差距大说明统计信息有偏差,优化器在误判。
  3. 按谓词形态创建复合索引:等值列 status 在前、范围列 created_at 在后,然后 ANALYZE TABLE 刷新统计。
  4. 重跑 EXPLAIN ANALYZE 逐步对比:索引范围扫描只读四万行,总耗时从 7900ms 掉到 45ms。
  5. 查 innodb_index_stats 里的索引体积,把读取收益与每次写入的放大代价放在同一张账上看,再决定索引去留。

排错提示

优化器不理新索引多半是复合索引列序不匹配谓词,等值列放最前、范围列放最后,再刷新统计;EXPLAIN ANALYZE 会真实执行一次,病态查询有真实代价,先在只读副本或限行数环境下诊断;预发有收益生产没有通常是数据分布不同,低峰期在生产跑一次 ANALYZE TABLE 让统计贴近现实再复查。

适用边界

EXPLAIN ANALYZE 只执行一次,计时包含一次热缓存运行,不能当基准测试用;复合索引列序是为特定查询服务的,帮到这条查询的索引对别条可能是死重,所以要看体积账。

代码示例

-- Before: full scan, 2.4M rows
EXPLAIN ANALYZE SELECT o.id FROM orders o
WHERE o.created_at >= '2026-10-01' AND o.status = 'shipped';
-- -> Table scan on o  (actual rows=2.4M, time=7900 ms)

ALTER TABLE orders ADD INDEX idx_status_created (status, created_at);
ANALYZE TABLE orders;

-- After: index range, 40k rows
EXPLAIN ANALYZE SELECT o.id FROM orders o
WHERE o.created_at >= '2026-10-01' AND o.status = 'shipped';
-- -> Index range scan on idx_status_created  (actual rows=40k, time=45 ms)