EXPLAIN执行计划优化:从type到Extra核心技巧

2026-06-12阅读 0热度 0
XPLA

执行计划深度剖析:从 typeExtra,榨干 EXPLAIN 的调优价值

你肯定用 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 ... 后会返回一张表,每列含义如下:

列名含义关键点
idSELECT 的标识序号数字越大越优先执行;相等则按顺序执行
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 等

表格内容虽全,日常重点关注几个字段即可:typekeyrowsExtra,以及联合索引场景下的 key_len

二、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连接使用 Block Nested Loop 缓存普通添加索引消除缓存
Impossible WHEREWHERE 条件恒假无需优化检查 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 直接影响被驱动表的读取次数,是衡量索引覆盖力的重要指标。

六、完整优化案例:从 0.5 秒到 0.02 秒

原始 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+?,Extra 中 filesort 消失(索引天然有序)。

效果:查询耗时从 0.5 秒降至 0.02 秒。

七、实用检查清单:快速诊断 EXPLAIN

阅读 EXPLAIN 时按以下顺序逐项排查:

  1. type: 是否存在 ALL 或 index?有则考虑加索引。
  2. key: 是否为 NULL?是则索引未被使用。
  3. rows: 是否远超预期?检查索引选择性。
  4. Extra: 出现 Using temporary 或 Using filesort ?优化排序和分组逻辑。
  5. filtered: 低于 30%?评估索引能否覆盖更多过滤条件。
免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策