MySQL的EXPLAIN不能直接分析INSERT/UPDATE/DELETE的执行计划,因其优化器未为DML生成访问路径;需改写为等价SELECT语句用EXPLAIN分析WHERE条件索引使用,并结合慢日志、SHOW PROFILE及InnoDB状态监控综合评估。

MySQL 的 EXPLAIN 不能直接分析 INSERT/UPDATE/DELETE 的索引执行计划 —— 这不是你用法错,而是 MySQL 优化器根本没为 DML 生成可展示的访问路径。
为什么对 DML 执行 EXPLAIN 会报错或返回无意义结果
MySQL 官方明确限定 EXPLAIN 仅支持 SELECT、UNION 和部分 SELECT INTO。对 UPDATE t SET x=1 WHERE y=5 直接加 EXPLAIN,5.5/8.0 等多数版本都会报 ERROR 1064 (42000);极少数 5.6 分支虽不报错,但返回 table: NULL、type: NULL、Extra: No tables used,完全无法反映 WHERE 条件是否走索引。
根本原因在于:DML 的行定位由存储引擎(如 InnoDB)直接完成,不经过优化器的“访问路径选择”阶段 —— 所以没有“执行计划”可解释。
正确做法:用等价 SELECT 语句反推 WHERE 条件的索引使用情况
DML 性能瓶颈通常卡在 WHERE 子句的行匹配上,而这部分逻辑和 SELECT 完全一致。必须手动构造对应查询:
-
UPDATE users SET status = 'done' WHERE created_at < '2024-01-01' AND type = 'pending'→ 改写为EXPLAIN SELECT id FROM users WHERE created_at < '2024-01-01' AND type = 'pending' -
DELETE FROM logs WHERE app_id = 123 AND ts < NOW() - INTERVAL 30 DAY→ 改写为EXPLAIN SELECT 1 FROM logs WHERE app_id = 123 AND ts < NOW() - INTERVAL 30 DAY - 重点看
type(是否为range/ref而非ALL)、key(是否命中预期索引)、rows(预估扫描行数是否合理)
DML 中真正影响性能的索引问题,往往不在“查”,而在“改”
即使 EXPLAIN SELECT 显示 WHERE 走了索引,DML 仍可能极慢 —— 因为索引维护本身开销巨大:
- 表上有 5 个二级索引,一次
UPDATE就要同步更新 5 次 B+ 树节点,IO 和锁竞争陡增 -
UPDATE t SET indexed_col = indexed_col + 1:既要用索引定位,又要重建索引项,还可能触发页分裂 - WHERE 条件走索引但返回 10 万行(如
UPDATE orders SET shipped = 1 WHERE status = 'paid'),优化器可能放弃索引,且逐行更新引发大量随机 IO - 虚拟列索引(如
JSON_EXTRACT(data, '$.user_id'))若字符集或排序规则不匹配,实际 UPDATE 会隐式转换失败,退化为全表扫描
验证 DML 是否真被索引加速的间接手段
没有直接命令,但组合方式更贴近真实场景:
- 开启慢日志:
SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 0;,捕获慢 DML,再对其 WHERE 部分做EXPLAIN SELECT - 用
SHOW PROFILE FOR QUERY N查看具体 DML 的handler_read_next(逐行读取次数)和select_full_range_join(全范围扫描指标) - 对高并发 DML 表,监控
Innodb_row_lock_waits和Innodb_buffer_pool_wait_free,比单纯看EXPLAIN更早暴露索引设计缺陷
别只盯着 key 列是否非空 —— DML 的索引有效性,最终得落在磁盘 IO 模式、锁粒度和缓冲池压力上。这些,EXPLAIN 一个字都不会告诉你。


















