MySQL的UPDATE默认加行级锁,具体为记录锁、间隙锁或临键锁,取决于WHERE条件是否命中索引、是否唯一及隔离级别。

UPDATE 语句默认加什么锁?
MySQL 的 UPDATE 在 InnoDB 引擎下默认走行级锁,但具体锁类型取决于 WHERE 条件是否命中索引、是否唯一、以及隔离级别。没索引的 WHERE 会升级为表锁;主键或唯一索引等值查询只锁匹配行;范围查询(如 WHERE id > 100)会加间隙锁(Gap Lock)或临键锁(Next-Key Lock),防止幻读。
为什么 UPDATE 会卡住甚至死锁?
常见原因有三类:
- 两个事务交叉更新同一组行,顺序不一致(比如事务 A 先改
id=1再改id=2,事务 B 反过来) - UPDATE 带子查询且子查询扫描大量未索引数据,持有锁时间过长
- WHERE 条件没走索引,InnoDB 退化成全表扫描+表级意向锁,阻塞其他 DML
死锁发生后,InnoDB 会自动选一个事务回滚(报错 Deadlock found when trying to get lock),另一个继续执行。
怎么避免 UPDATE 并发冲突?
核心思路是缩短锁持有时间 + 明确锁粒度:
- 确保 WHERE 字段有高效索引(至少是联合索引的最左前缀)
- UPDATE 尽量只改必要字段,避免大字段(如
TEXT)写入触发额外页分裂 - 业务层控制:用
SELECT ... FOR UPDATE显式加锁并校验前置状态,比盲目 UPDATE 更可控 - 高并发计数场景(如库存扣减),优先用原子操作:
UPDATE goods SET stock = stock - 1 WHERE id = 123 AND stock >= 1;
靠 WHERE 中的stock >= 1实现乐观检查,失败时应用层重试
READ COMMITTED 和 REPEATABLE READ 对 UPDATE 锁的影响
在 REPEATABLE READ(InnoDB 默认)下,UPDATE 会使用 Next-Key Lock,既锁记录又锁间隙,能防幻读但锁范围更大;READ COMMITTED 下只对已匹配的记录加记录锁,不加间隙锁,锁更轻、并发更好,但可能遇到幻读 —— 如果你的业务能接受这点,且 UPDATE 多为等值更新,可考虑切换隔离级别。
切换方式:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;注意全局修改需改
my.cnf 中的 transaction_isolation,且要评估对其他事务的影响。
真正难处理的不是锁本身,而是锁和业务逻辑耦合后产生的隐式依赖:比如一个 UPDATE 依赖另一个事务刚 INSERT 的记录,而那个 INSERT 还没提交 —— 这时候即使加了索引,也容易因锁等待超时或死锁暴露设计缺陷。










