乐观锁

业务场景:商品库存更新

1. 数据库表设计

我们在商品表 Product 中添加一个 version 字段,用来实现乐观锁。每当库存更新时,都会检查并更新该字段的值。

sql
CREATE TABLE product ( id BIGINT PRIMARY KEY, name VARCHAR(255), stock INT, version INT DEFAULT 1 -- 版本号字段,用来实现乐观锁 );

2. 实体类设计

在实体类中,通过 MyBatis-Plus 的 @Version 注解来标记 version 字段。

java
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.Version; @TableName("product") public class Product { private Long id; private String name; private Integer stock; @Version // 使用 MyBatis-Plus 的乐观锁注解 @TableField("version") // 映射数据库中的 version 字段 private Integer version; // Getter 和 Setter }

3. 乐观锁更新库存操作

当用户进行购买时,我们首先查询商品库存,然后尝试更新库存。如果更新时发现版本号发生变化,说明该商品的库存已经被其他用户修改过,我们就会拒绝这次操作。

java
public class ProductService { @Autowired private ProductMapper productMapper; public boolean purchase(Long productId, int quantity) { // 1. 查询商品信息 Product product = productMapper.selectById(productId); if (product == null || product.getStock() < quantity) { // 商品不存在或库存不足,购买失败 return false; } // 2. 扣减库存 product.setStock(product.getStock() - quantity); // 3. 使用乐观锁更新商品库存,version 字段自动处理 int updatedRows = productMapper.updateById(product); // 如果返回的更新行数为 0,说明版本号不一致,库存已经被其他用户修改 if (updatedRows == 0) { // 更新失败,可能因为并发冲突 return false; } return true; } }

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:

sql
UPDATE product SET stock = stock - 5, version = version + 1 WHERE id = 1 AND version = 1;
  • 如果 version = 1,更新操作会成功,库存减少 5,version 增加到 2。
  • 如果 version 已经不是 1,说明其他用户已经更新了该记录,更新操作将失败,返回 0 行更新。

6. 乐观锁的优势:

  • 减少锁竞争:与悲观锁不同,乐观锁不需要在更新时对数据进行加锁,适用于读多写少的场景,能够提高系统的并发性能。
  • 避免死锁:乐观锁没有锁竞争,因此避免了死锁的发生。
  • 业务简洁:只需在实体类中标注 @Version,MyBatis-Plus 会自动处理版本号的增减。

7. 乐观锁的不足:

  • 冲突时可能会丢失更新:当多个用户几乎同时操作相同数据时,可能会因为版本号不一致导致更新失败,需要在业务逻辑中捕获异常并做重试或其他处理。
  • 适用场景有限:乐观锁适用于冲突概率低的场景,冲突频繁时,可能反而导致频繁的更新失败,效率低下。
posted @   langpo  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示