执行计划深度解析:从 type 到 Extra,榨干 EXPLAIN 的价值

简介: 详解 MySQL EXPLAIN:从 type、key_len 到 Extra 各字段含义与优化技巧,用快递分拣等生动类比讲透执行计划。涵盖联合索引使用原则、filesort 本质、filtered 作用及实战调优案例,助你快速定位慢查询根源。

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

你肯定用过 EXPLAIN 看 SQL 的执行计划,但你有没有真正看全过?type 到底有几种取值?Extra 里的 Using indexUsing whereUsing temporaryUsing filesort 分别什么意思?key_len 怎么算?filtered 有什么用?今天我们就来把 EXPLAIN 的输出彻底讲透。

  • type 相当于分拣效率:最快的是“直接按门牌号送”(const),最慢的是“翻遍整个仓库”(ALL)。
  • possible_keys = 可能用的传送带,key = 实际选的传送带。
  • rows = 需要检查的包裹数量。
  • filtered = 初步分拣后还需要人工二次分拣的比例。
  • Extra = 额外操作标记,如“用了传送带但还要人工挑拣”(Using where)、“需要临时堆货”(Using temporary)。

一、EXPLAIN 输出列完整解读

我们用 EXPLAIN SELECT ... 会得到一张表,每个列的含义如下:

列名 含义 关键点
id SELECT 的标识序号 越大越先执行;相同则从上到下
select_type 查询类型 SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION 等
table 表名或别名 可能是临时表名()
partitions 匹配的分区 分区表时有用
type 连接类型(重要) 性能从好到差:system > const > eq_ref > ref > range > index > ALL
possible_keys 可能使用的索引 列出候选索引
key 实际使用的索引 如果 NULL 表示没用到索引
key_len 使用索引的长度(字节) 判断联合索引用了多少列
ref 索引列与哪个值比较 常量 const 或 列名
rows 预估需要扫描的行数 越大越差
filtered 存储引擎返回的行中满足剩余条件的比例 100% 最好
Extra 额外信息 Using index、Using where、Using temporary、Using filesort 等

二、type 详解:性能的关键指标

type 表示 MySQL 如何查找表中的行,按性能从最优到最差排序:

type 含义 示例 出现条件
system 系统表,只有一行 极少见 系统表或 const 的特例
const 最多匹配一行,用主键或唯一索引等值查询 WHERE id = 1 主键或唯一索引,且查询结果为常量
eq_ref 使用唯一索引进行关联,每个关联只返回一行 JOIN ... ON t1.id = t2.id 且 t2.id 是主键 被驱动表使用主键或唯一索引连接
ref 使用非唯一索引或前缀索引进行等值匹配 WHERE name = 'abc'(name 有普通索引) 索引列不是唯一或可为 NULL
range 索引范围扫描 WHERE id BETWEEN 1 AND 100IN>< 索引列上的范围条件
index 全索引扫描 索引覆盖但没过滤条件 遍历整个索引树
ALL 全表扫描(最差) 无索引或优化器认为全表更快 大表且无有效索引

优化目标:至少达到 range 级别,争取达到 refconst

案例

-- type = ALL 很差
EXPLAIN SELECT * FROM orders WHERE amount > 100;
-- 添加索引后 type 变为 range
ALTER TABLE orders ADD INDEX idx_amount(amount);

三、Extra 详解:优化器还做了什么?

Extra 列包含关于查询执行的额外信息,很多关键优化线索都在这里:

Extra 信息 含义 优劣 优化方向
Using index 使用了覆盖索引,不回表 ✅ 好 继续保持
Using where 存储引擎返回后在 Server 层过滤 🟡 普通 尝试将过滤条件移到索引中
Using temporary 使用了临时表(通常用于 GROUP BY 或 DISTINCT) ⚠️ 差 优化 GROUP BY/ORDER BY 或加索引
Using filesort 需要额外排序,不能利用索引排序 ⚠️ 差 对 ORDER BY 列加索引
Using index condition 使用索引下推(ICP) ✅ 好 MySQL 5.6+ 自动优化
Using join buffer 连接使用了 Buffer(Block Nested Loop) 🟡 普通 加索引避免 Buffer
Impossible WHERE WHERE 条件永远为假 无需优化 检查 SQL 逻辑
No tables used 没有 FROM 或 FROM DUAL - -

注意Using filesort 不是真的用文件,而是指无法利用索引排序,需要在内存或磁盘中排序。当排序结果集大时很慢。

案例

-- Using filesort
EXPLAIN SELECT * FROM orders ORDER BY create_time;
-- 加索引后 Using filesort 消失
ALTER TABLE orders ADD INDEX idx_create_time(create_time);

四、组合索引与 key_len 实战

key_len 表示 MySQL 在索引中实际使用的字节数。通过它可判断联合索引使用了多少列。

计算规则

  • 列长度:INT=4, BIGINT=8, DATE=3, TIMESTAMP=4, CHAR(n)=n×字符集字节数(utf8mb4=4),VARCHAR(n)=n×4+2。
  • 允许 NULL 额外 +1。

示例:索引 (user_id, log_date, type),user_id INT NOT NULL (4),log_date DATE NOT NULL (3),type TINYINT (1)。查询 WHERE user_id=1 AND log_date='2026-06-01'key_len=4+3=7,说明用到了前两列。

联合索引使用原则:最左前缀,且中间的列不能跳过。如果跳过了某列,后面的列不会被使用。


五、filtered 的作用

filtered 表示存储引擎返回的行中,满足剩余 WHERE 条件的比例(估算)。100% 表示所有返回行都满足条件。如果 filtered 很小(如 10%),说明索引过滤后还要过滤掉 90% 的行,回表成本高。

用法:在 JOIN 中,驱动表的 filtered 值直接影响被驱动表的读取次数。


六、实战案例优化全过程

原始 SQL

SELECT * FROM orders 
WHERE customer_id = 12345 
  AND status = 'PAID' 
  AND create_time > '2026-01-01'
ORDER BY create_time DESC
LIMIT 10;

原执行计划:type=ref,key=customer_id,rows=1000,Extra="Using where; Using filesort"。

问题分析

  • 用了 customer_id 索引,但 status 和 create_time 过滤在回表后执行。
  • filesort 因为 create_time 没在索引中用于排序。

优化方案:建立联合索引 (customer_id, status, create_time)

新执行计划:type=ref,key=联合索引,key_len=4+?+3,Extra=无 filesort(因为索引已排序)。

效果:查询从 0.5 秒降到 0.02 秒。


七、总结与实用检查清单

阅读 EXPLAIN 时按以下顺序检查:

  1. type:是否出现了 ALL 或 index?如果是,考虑加索引。
  2. key:是否为 NULL?是则索引没用上。
  3. rows:是否远大于预期?检查索引选择性。
  4. Extra:是否出现 Using temporary 或 Using filesort?优化排序和分组。
  5. filtered:是否低于 30%?检查索引是否能覆盖更多过滤条件。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~

相关文章
|
1月前
|
SQL JSON 移动开发
SQL派生表优化实战:从物化机制到LATERAL JOIN的完整进阶
很多人只知道“子查询改JOIN就快了”,但不知道为什么,也不知道什么时候该改、什么时候不该改。本文从派生表的物化机制出发,拆解临时表膨胀、索引失效的根因,通过真实案例对比派生表、CTE、LATERAL JOIN三种写法的性能差异,帮助读者从“知道现象”升级到“理解原理”。
|
1月前
|
存储 固态存储 关系型数据库
从月账单5万到3.5万:云数据库成本优化的完整复盘
上云本应是降本增效,但很多企业上云之后,账单反而越滚越大。实例规格买高了、历史数据堆在SSD上、测试环境没人关、过期快照没清理——每一笔费用都在悄悄累积。本文从云账单的三大“黑洞”出发,拆解云成本失控的根因,给出实例降配、冷热数据分层、僵尸资源清理三条可落地的优化路径,帮助DBA和运维工程师用数据驱动成本优化,让每一分钱都花在刀刃上。
|
1月前
|
SQL 运维 监控
慢查询日志的“高级用法”:从找慢SQL到做容量规划
慢查询日志是DBA最熟悉的工具,但大多数人只用它来找“跑得慢的SQL”。如果只做到这一步,你只用了慢查询日志20%的价值——剩下的80%是建立性能基线、预测容量瓶颈、评估优化效果、发现潜在风险。本文从慢查询日志的进阶用法出发,讲解如何通过持续记录慢查询建立性能基线、如何通过慢查询趋势预测容量瓶颈、如何将慢查询日志从“故障排查工具”升级为“容量规划工具”,帮助读者从“出了问题再查”升级到“看着趋势主动调整”。
|
1月前
|
SQL JSON 数据库
SQL性能调优进阶:从“会看执行计划”到“会诊断整个系统”
一条SQL慢,可能有一百种原因——SQL写法有问题、索引没建对、统计信息过旧、参数没调好、磁盘I/O满了、内存不够、网络抖动……很多DBA的做法是“先查SQL”,但真正的问题往往不在SQL本身。本文从“分层诊断”的思路出发,建立一套从SQL层→数据库层→操作系统层的逐层排查方法论,帮助读者在面对性能问题时不再“眉毛胡子一把抓”。
|
1月前
|
SQL 关系型数据库 MySQL
UNION vs UNION ALL:一个“ALL”字,性能差了一个数量级
UNION和UNION ALL的区别很多人知道,但INTERSECT和EXCEPT的执行机制、性能差异,以及如何用JOIN和子查询替代,很多人并不清楚。本文从集合操作的执行计划出发,拆解UNION去重的“隐形代价”、INTERSECT与INNER JOIN的本质差异、EXCEPT与NOT EXISTS的性能对比,并通过真实案例展示集合操作在业务场景中的正确用法与避坑指南,帮助读者从“会写集合操作”升级到“理解集合操作的底层逻辑”。
|
1月前
|
SQL JSON 算法
SQL执行计划的“成本模型”:读懂cost,理解优化器为什么选这个计划
EXPLAIN能告诉你优化器选了哪个执行计划,但说不出它为什么这么选——明明有索引它却走全表扫描,明明A计划更快它却选了B计划。优化器不靠猜,它靠一套成本模型(Cost Model)做决策。本文从优化器的成本模型出发,拆解cost的构成(IO_cost、CPU_cost、memory_cost),讲解如何通过EXPLAIN FORMAT=JSON和OPTIMIZER_TRACE看到优化器的“思考过程”,并通过真实案例展示优化器“算错账”的根因,帮助读者从“知道选了谁”升级到“理解为什么选它”。
|
2月前
|
存储 人工智能 关系型数据库
湖库一体:2026年数据库架构的“终极答案”还是新瓶装旧酒?
2026年6月,OceanBase发布湖库一体AI数据库,阿里云PolarDB年初已推出AI数据湖库(Lakebase),Databricks也在6月推出了LTAP架构。“湖库一体”成为2026年数据库圈最热的概念之一。本文从湖库一体的概念定义出发,拆解其技术原理,对比“湖仓一体”与“湖库一体”的差异,分析三大厂商的落地路径,并讨论这一趋势对DBA和架构师的现实意义。
|
3月前
|
人工智能 Cloud Native 关系型数据库
MySQL 8.4 LTS来了!从8.0到8.4,DBA必须知道的5个核心变化
MySQL 8.0社区版将于2026年结束生命周期,8.4 LTS作为首个长期支持版本,提供5年超长支持周期(至2031年)。本文从InnoDB并行查询、Redo Log动态容量、默认认证插件变更、参数默认值调整、云原生适配五个维度,梳理DBA升级前必须掌握的核心变化,并提供升级检查清单。
|
3月前
|
SQL 运维 自然语言处理
国产向量数据库有哪些?两大技术流派深度对比与选型指南
向量数据库是2026年数据库领域增长最快的细分赛道之一。本文从RAG应用和企业知识库的实际需求出发,系统梳理国产向量数据库的两大技术流派——独立向量数据库与融合型向量数据库,深入对比两者的架构差异、适用边界和选型逻辑。
|
3月前
|
SQL 人工智能 自然语言处理
Vibe Coding 是什么?当“感觉编程”遇上数据库
Vibe Coding是2026年编程圈最火的概念之一,指开发者通过自然语言描述“感觉”或“意图”,由AI自动生成代码、调试、优化。本文从Vibe Coding的起源讲起,分析它如何改变数据库开发方式:从手写SQL到自然语言查询、从人工调索引到AI推荐、从经验运维到智能诊断。探讨这项趋势对DBA职业的影响,并给出拥抱变化的实用建议。技术会变,但人的判断力、审美和业务理解才是长期竞争力。