MySQL不支持直接EXPLAIN UPDATE,需转为等价SELECT分析;重点观察type、key、rows三列判断索引是否生效,并注意函数、类型转换、OR、联合索引最左原则及统计信息过期等问题。

EXPLAIN 不能直接用于 UPDATE,得先转成 SELECT
MySQL 和大多数主流数据库不支持对 UPDATE 语句直接执行 EXPLAIN(会报语法错误)。想看更新操作是否走索引,必须把 WHERE 条件和涉及的列“翻译”成等价的 SELECT 语句再分析。
例如原始语句是:
UPDATE users SET status = 'active' WHERE created_at > '2025-01-01' AND user_id = 123;
对应分析用的 SELECT 应该是:
EXPLAIN SELECT * FROM users WHERE created_at > '2025-01-01' AND user_id = 123;
关键点在于:保持 WHERE 条件完全一致,且查询字段尽量覆盖 UPDATE 中实际需要定位的行(不需要真查出所有列,但 * 最稳妥)。
- 别漏掉任何
AND条件——少一个就可能让优化器选错索引 - 如果
UPDATE里用了子查询或 JOIN,SELECT也得还原结构,否则执行计划失真 - 某些数据库(如 PostgreSQL)支持
EXPLAIN UPDATE,但 MySQL 不行;别被文档版本误导
重点盯死 type、key、rows 这三列
EXPLAIN 输出里,真正暴露索引失效的是这三项:
-
type是ALL或index:说明在全表扫或全索引扫,没走有效索引查找 -
key是NULL:压根没选中任何索引,哪怕表上有几十个索引也没用 -
rows明显大于你预期匹配的行数(比如表有 10 万行,rows=98234):说明索引没过滤掉数据,效率接近全扫
特别注意:即使 key 显示了某个索引名,但如果 rows 很大,大概率是这个索引选择性太低,或者条件没命中最左前缀。这时候得结合索引定义再判断。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
UPDATE 常见的索引失效写法,比 SELECT 更隐蔽
UPDATE 的 WHERE 条件稍有不慎,索引就废了。这些写法在日常 SQL 里高频出现,但容易被忽略:
- 对索引列用函数:
WHERE DATE(created_at) = '2025-06-01'→ 改成WHERE created_at >= '2025-06-01' AND created_at - 隐式类型转换:
WHERE user_id = '123'(user_id 是INT)→ 改成WHERE user_id = 123 - OR 条件混用:
WHERE id = 123 OR email LIKE '%@qq.com'→ 拆成两个独立UPDATE,或补上email索引并改用右模糊LIKE 'xxx@qq.com' - 联合索引跳过最左列:
INDEX (tenant_id, status, created_at),但写成WHERE status = 'pending' AND created_at > NOW()→ 不走索引;必须带上tenant_id
UPDATE 的执行代价比 SELECT 高——它要加锁、写 binlog、触发二级索引维护。一旦索引失效,不仅慢,还容易引发锁等待甚至死锁。
别忘了统计信息过期这个“幽灵原因”
即使 WHERE 写得完全合规、索引也建对了,EXPLAIN 仍可能显示 type=ALL 或 rows 夸大其词。这时候大概率是表的统计信息陈旧了。
MySQL 依赖统计信息估算行数,决定是否用索引。数据批量导入、长期未 ANALYZE 后,优化器会误判索引效率,主动放弃使用。
- 手动更新:
ANALYZE TABLE users; - 检查是否启用自动更新:
SHOW VARIABLES LIKE 'innodb_stats_auto_recalc';(应为ON) - 云数据库(如阿里云 RDS、AWS RDS)通常默认开启,但自建库常被忽略
统计信息不是“设一次就永远有效”的配置。尤其当某张表日增百万级数据时,隔天不 ANALYZE 就可能让原本高效的 UPDATE 变成定时锁表炸弹。










