业务场景:商品库存更新
1. 数据库表设计
我们在商品表 Product
中添加一个 version
字段,用来实现乐观锁。每当库存更新时,都会检查并更新该字段的值。
2. 实体类设计
在实体类中,通过 MyBatis-Plus 的 @Version
注解来标记 version
字段。
3. 乐观锁更新库存操作
当用户进行购买时,我们首先查询商品库存,然后尝试更新库存。如果更新时发现版本号发生变化,说明该商品的库存已经被其他用户修改过,我们就会拒绝这次操作。
4. 乐观锁执行过程
假设商品的 id = 1
,初始库存为 10,版本号为 1。
-
用户 A:
- 查询库存:库存为 10,版本号为 1。
- 扣减库存:购买 5 个,库存剩余 5,版本号自增为 2。
- 执行
updateById
,更新成功,库存变为 5,版本号变为 2。
-
用户 B:
- 查询库存:库存为 10,版本号为 1。
- 扣减库存:购买 5 个,库存剩余 5,版本号自增为 2。
- 执行
updateById
,但此时商品的版本号已经被用户 A 更新为 2。 - 用户 B 的更新操作失败,
updateById
返回更新行数为 0,库存没有被更新。
5. SQL 生成过程
在执行 updateById
时,MyBatis-Plus 会自动生成类似如下的 SQL:
- 如果
version = 1
,更新操作会成功,库存减少 5,version
增加到 2。 - 如果
version
已经不是 1,说明其他用户已经更新了该记录,更新操作将失败,返回 0 行更新。
6. 乐观锁的优势:
- 减少锁竞争:与悲观锁不同,乐观锁不需要在更新时对数据进行加锁,适用于读多写少的场景,能够提高系统的并发性能。
- 避免死锁:乐观锁没有锁竞争,因此避免了死锁的发生。
- 业务简洁:只需在实体类中标注
@Version
,MyBatis-Plus 会自动处理版本号的增减。
7. 乐观锁的不足:
- 冲突时可能会丢失更新:当多个用户几乎同时操作相同数据时,可能会因为版本号不一致导致更新失败,需要在业务逻辑中捕获异常并做重试或其他处理。
- 适用场景有限:乐观锁适用于冲突概率低的场景,冲突频繁时,可能反而导致频繁的更新失败,效率低下。