EXPLAIN 输出总览
EXPLAIN 输出一张执行计划表,覆盖访问路径、索引选用、行数估算与附加动作四类信息。读懂它等于读懂优化器的选择:为什么这张表被全表扫描、为什么索引没被用上、为什么多了一次排序。它给出的是估算,行数来自统计信息,可能与真实执行差一个数量级,需要精确数字就用能实际执行的版本。所有优化动作都应该以这张表为依据,而不是凭感觉加索引。
基本信息
| 用途 | 查看语句的执行计划与成本估算 |
|---|---|
| 输出内容 | 序号、查询类型、访问对象、访问类型、候选与实际索引、行数与过滤比例、附加信息 |
| 访问类型顺序 | 常量 → 唯一索引等值 → 普通索引等值 → 范围 → 全索引扫描 → 全表扫描 |
| 行数口径 | 估算值,来自统计信息 |
| 附加信息 | 额外排序、临时表、连接缓冲、覆盖索引 |
| 实测手段 | 8.0.18 起可实际执行语句获取真实行数 |
典型使用场景
- 优化慢语句的第一步:先确认扫描路径,再决定加索引还是改写法
- 上线评审:确认新语句是否存在全表扫描或额外排序
- 索引验收:建索引前后对比使用的索引与扫描行数
- 分页查询排查:排序是否借用了索引顺序
- 连接查询排查:被驱动表是否走索引、有无连接缓冲
- 核心语句定期巡检,捕捉执行计划的变化
基础用法
基础用法
EXPLAIN SELECT o.id, o.amount FROM appdb.orders o
WHERE o.user_id = 12345 AND o.status = 1
ORDER BY o.created_at DESC LIMIT 20; # 常见列表查询的计划样例
EXPLAIN FORMAT=JSON SELECT o.id FROM appdb.orders o # 结构化输出,含成本明细
WHERE o.user_id = 12345;读计划三步走
读计划三步走
# 第一步:看访问类型,出现全表扫描就是首要问题
# 第二步:看实际使用的索引与索引长度,确认复合索引用到几列
# 第三步:看扫描行数与附加信息,确认排序、临时表与连接缓冲
EXPLAIN SELECT id FROM appdb.orders
WHERE status = 1 ORDER BY created_at DESC LIMIT 10;关键列读法
| 列 | 含义 | 判读要点 |
|---|---|---|
| id 与 select_type | 查询块序号与类型 | 出现子查询要评估能否改写 |
| table 与 partitions | 访问对象与命中分区 | 分区裁剪效果在这里体现 |
| type | 访问类型,性能关键指标 | 优先级最高,全表扫描应优先优化 |
| possible_keys | 候选索引 | 为空说明没有可用索引 |
| key | 实际使用的索引 | 为 NULL 说明索引没被用上 |
| key_len | 索引使用长度 | 可推断复合索引使用了几列 |
| rows 与 filtered | 估算扫描行数与过滤比例 | 行数巨大是成本主体 |
| Extra | 附加信息 | 出现额外排序与临时表要优先处理 |
EXPLAIN 是估算而不是实测:行数来自统计信息,可能与真实执行差一个数量级;需要精确数字时用能实际执行的版本,它会给出真实行数与耗时。
输出里信息量最大的是访问类型、实际使用的索引与附加信息三项,但只确认用上了索引还不够:联合索引前缀不对时索引虽然命中,过滤效果依然很差。
实测分析会真正执行语句并产生负载,对生产环境的写语句风险很高;排查应安排在只读从库或业务低峰期进行。
计划解读顺序
- 先确认有没有全表扫描,全表扫描通常是第一优先项
- 再看实际使用的索引与索引长度,判断复合索引利用率
- 再看扫描行数与过滤比例,评估成本量级
- 然后看附加信息里的排序、临时表与连接缓冲
- 最后结合统计信息是否新鲜,判断计划是否可靠
- 优化完成后重新生成计划,做前后对比
执行计划会随数据量、数据分布与统计信息变化而变化:上线时看着正常不代表长期正常,核心语句应定期取样比对,发现计划漂移及时处理。