登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  MySQL

MySQL EXPLAIN 怎样分析 UPDATE 或 DELETE 的执行计划

来源:17golang原创

时间:2026-10-23 04:26:49 343浏览 收藏

MySQL 的 UPDATE 和 DELETE 并不需要“先执行再看结果”。在真正改数据前,可以先用 EXPLAIN 观察优化器准备怎样找到目标行:走哪个索引、预计扫描多少行、过滤条件放在哪里。最重要的边界是:这份计划是读取路径的估算,不是已经修改的行数,也不等于真实运行耗时。

官方地址:https://dev.mysql.com/doc/refman/8.4/en/explain.html

要点速览
  • EXPLAIN UPDATE 和 EXPLAIN DELETE 用来预估访问路径,不会直接执行对应的写入动作。
  • 先看 type、key、rows、filtered,再决定是否需要改索引或缩小范围。
  • 计划中的 rows 是估算值;生产 DML 仍要配合分批、备份、事务和回滚方案。

先把 UPDATE 和 DELETE 变成可读的计划

假设订单表按客户和创建时间筛选,先把要执行的语句原样放到 EXPLAIN 后面。下面的 SQL 只展示计划读取路径,关键是不要为了“看看效果”直接去掉 EXPLAIN。

-- 先查看 UPDATE 的读取路径,不会执行这次状态修改。
EXPLAIN
UPDATE orders
SET status = 'archived'
WHERE customer_id = 1001
  AND created_at 

MySQL 文档把 EXPLAIN 的适用对象扩展到 SELECT、UPDATE、DELETE、INSERT 和 REPLACE。因此,写入语句的第一道门可以统一成“先看读路径,再决定写范围”。

MySQL EXPLAIN 分析 UPDATE 和 DELETE 时从条件、索引到目标行的静态结构关系图
图1:DML 执行计划结构说明图,展示条件、候选索引、访问节点和目标行之间的静态关系,不是数据库截图。

按四个字段判断计划是否值得执行

计划输出字段很多,排查 UPDATE 或 DELETE 时先抓住四个高价值字段。它们要放在一起看,不能只看到 type=ALL 就立即断定语句一定错误,也不能只看到 key 有值就认为风险已经消失。

字段重点问题常见判断
type怎样访问表const、eq_ref、ref 通常比 ALL 更容易控制范围;仍要结合数据分布。
possible_keys哪些索引可能有用为空不一定代表语句错,但说明优化器没有发现可用索引。
key最终选了哪个索引为空时重点检查条件、类型转换、函数包裹和索引顺序。
rows、filtered预计读多少、剩多少两者结合估算读取压力;它们是统计信息推算,不是最终受影响行数。

例如 key 有值但 rows 仍很大,可能是索引选择性不足,或者复合索引的最左列不能有效缩小范围。反过来,rows 看起来很小,也不能跳过业务条件确认,因为统计信息可能已经过期。

全表扫描时先查条件和索引的对应关系

type=ALL 表示全表扫描。对小表或确实要处理大多数行的批量任务,它未必是问题;但对生产大表的 UPDATE、DELETE,它往往意味着锁持有时间、日志量和回滚成本会一起上升。

先检查三个方向:过滤列是否有索引;复合索引的列顺序是否让等值条件和范围条件落在可利用位置;条件两侧的数据类型是否一致。不要为了让计划出现索引就盲目新增索引,写入还会带来索引维护成本。

-- 用实际写入条件设计复合索引时,先把等值列放在前面。
CREATE INDEX idx_orders_customer_created
ON orders (customer_id, created_at);

-- 建索引后重新查看计划,确认读取范围是否真的缩小。
EXPLAIN
UPDATE orders
SET status = 'archived'
WHERE customer_id = 1001
  AND created_at 

如果条件对列使用函数、隐式转换,或者把范围写在无法利用索引的位置,新增索引也可能没有预期效果。先让计划解释“为什么读这些行”,再决定是否改 SQL。

复杂语句用 JSON 计划补充判断

表连接、派生表或多个条件组合在一起时,表格输出可能不够直观,可以改用 JSON 格式。它适合查看更完整的访问节点和成本信息,但仍然属于优化器估算,不能把 JSON 中的数字当作真实影响行数。

-- JSON 计划适合复杂 DML;仍然只做计划预估,不执行删除。
EXPLAIN FORMAT=JSON
DELETE FROM orders
WHERE customer_id = 1001
  AND created_at 

生产执行前,我会把计划和数据安全动作分开:先在测试或影子环境确认范围,正式任务再加上明确的主键边界,必要时按主键区间分批,并记录每批影响行数。对于不可逆的删除,备份或可恢复副本应先于 SQL 调优。

MySQL 写入前由执行计划、范围条件、分批策略和回滚边界组成的静态安全关系图
图2:写入前安全边界说明图,展示计划估算如何连接到范围、分批与恢复策略,不是运行监控截图。

执行前的简短检查清单

  1. 语句先加 EXPLAIN,确认没有误删 WHERE。
  2. 记录 type、key、rows、filtered,不要只看一个字段。
  3. 确认计划中的索引和条件与本次数据范围一致,注意统计信息和参数类型。
  4. 大表写入优先按主键或时间边界分批,并为失败批次保留恢复路径。
  5. 把计划估算与实际影响行数分开记录,避免把 rows 当成最终结果。

常见问题

EXPLAIN UPDATE 会不会真的修改数据? 不会,它返回优化器对读取路径的计划;真正执行前仍要检查权限、锁和数据范围。

key 为空是不是一定要加索引? 不一定。小表、需要读取大多数行的任务或统计信息判断都可能让全表扫描合理,先结合数据规模和业务范围判断。

rows 能不能当成 DELETE 最终删除行数? 不能。它是估计要读取的行数,最终影响行数还取决于条件、并发变化和实际数据。

为什么有索引仍然扫描很多行? 常见原因是选择性低、复合索引顺序不合适、条件发生类型转换或函数计算;应结合 key、rows 和完整条件一起排查。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>