UPDATE比SELECT慢主因是定位行耗时(如全表扫描),且需加锁、写undo log、更新二级索引等;EXPLAIN不直接支持UPDATE,应改用SELECT模拟WHERE条件分析执行计划。

UPDATE 为什么比 SELECT 还慢?
UPDATE 慢,往往不是因为“改数据”本身耗时,而是数据库先得 WHERE 条件里把要改的行找出来——这个查找过程如果没走索引,就是全表扫描。更糟的是,UPDATE 还要加行锁、写 undo log、更新二级索引、刷脏页……每一步都依赖定位效率。所以,UPDATE 慢的本质,90% 以上是定位慢,不是修改慢。
EXPLAIN 看不到 UPDATE?用 SELECT 模拟定位逻辑
MySQL 的 EXPLAIN 不支持直接分析 UPDATE 语句(5.7+ 支持 EXPLAIN UPDATE,但兼容性和可读性差)。实际排查时,应提取其 WHERE 条件,套进 SELECT 中跑 EXPLAIN:
EXPLAIN SELECT * FROM orders WHERE status = 'pending' AND created_at < '2026-01-01';
重点关注:
-
type是ALL或index?说明没走有效索引 -
key为NULL?确认索引未被选用 -
rows值远大于实际匹配行数?可能是统计信息过期或索引选择性差 -
Extra出现Using where; Using index condition是好的;若只有Using where,说明索引没覆盖过滤条件
哪些 WHERE 条件会让索引失效,UPDATE 就必然慢
即使你给字段建了索引,UPDATE 仍可能退化为全表扫描。常见踩坑点:
- 对索引列用函数:
WHERE DATE(created_at) = '2026-05-01'→ 改成WHERE created_at >= '2026-05-01' AND created_at - 隐式类型转换:
WHERE user_id = 123(user_id是VARCHAR)→ 必须传字符串'123',否则触发全表扫描 -
LIKE左模糊:WHERE name LIKE '%abc'→ B-tree 索引无法利用,考虑全文索引或倒排结构 -
OR连接非索引列:WHERE status = 'paid' OR amount > 1000(amount无索引)→ 优化器大概率放弃索引,改用UNION ALL拆解 - 联合索引顺序错位:
INDEX idx_status_time (status, created_at),但查询是WHERE created_at > '2026-01-01'→ 最左前缀不满足,索引失效
UPDATE 慢还可能和索引本身无关,但容易被忽略
即使 EXPLAIN 显示走了索引,UPDATE 仍可能卡住,原因常藏在执行路径之外:
- 目标行被其他事务锁住:
SHOW ENGINE INNODB STATUS查TRANSACTIONS部分,看是否有lock wait - 二级索引太多:每改一行,所有相关二级索引都要更新 → 检查
SHOW INDEX FROM table_name,删掉长期不用的索引 - 大字段(
TEXT/BLOB)被包含在 UPDATE 列表中:即使没改,也会触发额外的磁盘 IO 和日志写入 -
innodb_buffer_pool_size太小:大量数据页不在内存,UPDATE 前需频繁从磁盘载入 → 观察Innodb_buffer_pool_reads是否持续上升 - redo log 刷盘阻塞:高并发 UPDATE 下,
innodb_log_file_size过小导致频繁 checkpoint,表现为偶发性延迟
真正难缠的慢 UPDATE,往往卡在“看起来用了索引,但实际锁住了、IO 溢出了、或者日志写崩了”——别只盯着 EXPLAIN 的 key 字段是否非空。

















