如何实现悲观锁控制_mysql并发更新策略

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

在 MySQL 中实现悲观锁控制并发更新,核心是借助数据库的行级锁机制,在事务中显式加锁,确保同一时间只有一个事务能修改某条记录。关键在于合理使用

SELECT ... FOR UPDATE
SELECT ... LOCK IN SHARE MODE
,并配合事务隔离级别(推荐
REPEATABLE READ
)和正确提交/回滚逻辑。

明确加锁场景与语句写法

悲观锁不是自动生效的,必须在事务内主动发起带锁查询:

排他锁(FOR UPDATE):适用于“读取后要更新”的典型场景,如扣减库存、转账。它会锁定查到的行(含间隙),阻止其他事务对该行做
UPDATE
DELETE
或再次
FOR UPDATE
共享锁(LOCK IN SHARE MODE):适用于需校验但不立即修改的场景,如检查余额是否充足后再决定是否下单。其他事务仍可加共享锁,但不能加排他锁。 注意:
FOR UPDATE
REPEATABLE READ
下默认使用 next-key lock(行锁 + 间隙锁),能防止幻读;若只查主键且值存在,则退化为行锁。

保证事务完整性与锁生命周期

锁只在事务内有效,事务结束(

COMMIT
ROLLBACK
)时自动释放:

务必开启显式事务(
BEGIN
START TRANSACTION
),避免语句自动提交导致锁瞬间释放。
加锁查询与后续
UPDATE
必须在同一个事务中,否则锁已失效。
避免长事务:锁持有时间越长,并发阻塞越严重。业务逻辑应尽量精简,不要在事务中做耗时操作(如远程调用、文件读写)。

规避常见陷阱

很多并发问题源于对锁行为理解偏差:

没走索引 = 表锁:如果
FOR UPDATE
WHERE
条件未命中索引,MySQL 可能升级为表级锁,极大降低并发能力。务必确认执行计划(
EXPLAIN
)显示走了索引。
锁范围超出预期:例如
SELECT * FROM t WHERE id > 100 FOR UPDATE
会锁住所有满足条件的行及之间的间隙,可能影响无关记录的插入。
死锁风险:多个事务以不同顺序访问多行时易触发死锁。MySQL 会自动检测并回滚其中一个事务(报错
Deadlock found
),应用层需捕获并重试。

简单示例:安全扣减库存

假设商品表

products(id, stock)
,扣减 ID=123 的库存 1 件:

START TRANSACTION;
SELECT stock FROM products WHERE id = 123 FOR UPDATE;
-- 应用层判断 stock >= 1
UPDATE products SET stock = stock - 1 WHERE id = 123;
COMMIT;

若库存不足,应在事务内直接

ROLLBACK
并返回错误,避免无效更新。

相关推荐