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

8次阅读

MySQL 悲观锁需在事务中用 SELECT…FOR UPDATE 或 LOCK IN SHARE MODE 显式加锁,配合 REPEATABLE READ 隔离级;锁随事务结束释放,须避免无索引查询、锁范围过大及死锁。

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

在 MySQL 中实现悲观锁控制并发更新,核心是借助数据库的行级锁机制,在事务中显式加锁,确保同一时间只有一个事务能修改某条记录。关键在于合理使用 SELECT …… FOR UPDATESELECT …… LOCK IN SHARE MODE,并配合事务隔离级别(推荐 REPEATABLE READ)和正确提交 / 回滚逻辑。

明确加锁场景与语句写法

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

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

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

锁只在事务内有效,事务结束(COMMITROLLBACK)时自动释放:

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

规避常见陷阱

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

  • 没走索引 = 表锁 :如果 FOR UPDATEWHERE 条件未命中索引,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 并返回错误,避免无效更新。

text=ZqhQzanResources