explain 不能用于 dml 语句,因 mysql 优化器仅对 select/union 等查询生成可展示的访问路径计划;dml 的行定位由存储引擎直接处理,故报语法错误;应通过等价 select 语句反推索引使用情况。

EXPLAIN 不能用于 DML 语句(INSERT/UPDATE/DELETE)的执行计划分析,MySQL 官方不支持对这些语句直接使用 EXPLAIN。想确认索引在写操作中是否被有效利用,必须换路径。
为什么不能对 INSERT/UPDATE/DELETE 用 EXPLAIN
MySQL 的 EXPLAIN 仅适用于 SELECT、UNION 和某些 SELECT ... INTO 语句。对 INSERT、UPDATE 或 DELETE 执行 EXPLAIN 会报错:ERROR 1064 (42000): You have an error in your SQL syntax。这是因为优化器在写操作中不生成可展示的“访问路径”计划,而是由存储引擎直接处理行定位逻辑。
替代方案:从 SELECT 入手反推索引使用情况
绝大多数索引失效问题实际发生在写操作的 WHERE 条件匹配阶段(如 UPDATE ... WHERE x = ?),这部分逻辑与等价的 SELECT 完全一致。因此应构造对应查询进行诊断:
- 把
UPDATE t SET y = 1 WHERE a = 5 AND b > 10改写为EXPLAIN SELECT * FROM t WHERE a = 5 AND b > 10 - 把
DELETE FROM t WHERE status = 'draft' AND created_at 改写为 <code>EXPLAIN SELECT id FROM t WHERE status = 'draft' AND created_at - 重点看
type是否为ALL、key是否为NULL或非预期索引、rows是否远超预期
真正影响 DML 性能的索引问题场景
写操作的性能瓶颈往往不在“是否走索引”,而在索引维护开销本身。以下情况会导致 DML 明显变慢,即使 WHERE 条件用了索引:
- 表上有大量二级索引:每条
INSERT/UPDATE/DELETE都要同步更新所有相关索引,IO 和 CPU 成倍增长 - WHERE 条件命中索引但返回大量行(如
UPDATE users SET flag=1 WHERE city='beijing'):优化器可能选全表扫描,且逐行更新引发大量随机 IO - UPDATE 涉及索引列本身(如
UPDATE t SET indexed_col = indexed_col + 1):不仅查要走索引,改还要重建索引节点,锁和日志压力陡增 - 虚拟列索引(如 JSON 提取字段)存在隐式排序规则不匹配:即便
EXPLAIN SELECT显示用了索引,实际 UPDATE 可能因字符集转换失败而退化
检查索引是否被 DML 实际使用的间接方法
没有直接命令,但可通过组合手段验证:
- 开启
slow_query_log并设置long_query_time = 0,捕获慢 DML,再对其中 WHERE 部分做EXPLAIN SELECT - 用
SHOW PROFILE FOR QUERY N查看具体 DML 的select_full_range_join或handler_read_rnd_next指标是否异常高(说明回表或全扫严重) - 对比
Handler_read_*状态变量:执行前后运行SHOW STATUS LIKE 'Handler_read%',若Handler_read_rnd_next增幅巨大,基本确认走了全表或低效索引扫描
真正难排查的是那些“看似走了索引,但 DML 还是慢”的情况——比如联合索引中间列用了范围条件,导致后续列无法过滤;或者虚拟列索引因排序规则隐式转换失效。这些不会在 EXPLAIN 里报错,但会让 rows 字段暴露真实扫描量。











