mysql如何处理UPDATE语句中的锁与并发问题

来源:这里教程网 时间:2026-02-28 20:46:45 作者:

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 还没提交 —— 这时候即使加了索引,也容易因锁等待超时或死锁暴露设计缺陷。

相关推荐