java+mybatis用version字段实现乐观锁库存扣减:读时不加锁,更新时校验id、version和库存,失败重试;需建version字段并索引,sql或mybatis-plus自动注入校验条件。

Java + MyBatis 实现高并发库存扣减,核心是用版本号(version)字段做乐观锁控制——读时不加锁,更新时校验版本,冲突自动失败,靠重试保障最终一致性。不依赖数据库行锁,吞吐高、无死锁风险。
数据库表必须加 version 字段
在库存表中新增整型 version 字段,初始值设为 0 或 1(推荐 0),并确保该字段参与所有更新条件:
- 建表示例:
ALTER TABLE inventory ADD COLUMN version INT NOT NULL DEFAULT 0; - 务必为 version 字段建立索引(非必需但建议),避免 WHERE 条件全表扫描
- version 只用于校验,不参与业务逻辑计算,也不对外暴露
MyBatis Mapper 接口与 SQL 要带 version 校验
更新语句必须同时满足三个条件:id 匹配、当前 version 等于读取值、库存充足。例如:
- XML 写法(推荐):
<update id="deductStock"><br> UPDATE inventory<br> SET stock = stock - #{quantity}, version = version + 1<br> WHERE id = #{id} AND version = #{oldVersion} AND stock >= #{quantity}<br></update> - 返回值为受影响行数:等于 1 表示成功;等于 0 表示版本不匹配或库存不足,需业务层处理
- 避免使用
SELECT ... FOR UPDATE,那是悲观锁,会阻塞并发
Java 服务层要包含读-判-更-重试闭环
不能只调一次 update,必须封装“读取 → 判断库存 → 尝试更新 → 失败重试”完整链路:
- 先查出商品当前 stock 和 version:
Inventory inventory = mapper.selectById(id); - 检查库存是否足够(应用层前置校验,减少无效 DB 请求)
- 调用带 version 的 update 方法,根据返回值判断是否成功
- 失败时递归或循环重试(建议最多 3 次),每次重试前重新 select 获取最新 version
- 重试间隔可加随机微延迟(如 1–10ms),缓解瞬时碰撞
MyBatis-Plus 可用 @Version 注解简化开发
如果项目已集成 MP,直接用注解替代手写 SQL,框架自动注入 version 条件:
- 实体类字段标注:
@Version private Integer version; - 配置乐观锁插件(Spring Boot 配置类中启用)
- 调用
updateById()或update()时,MP 自动追加AND version = ?条件,并将 version 字段 +1 - 注意:MP 默认 version 类型为 Integer,若数据库用 BIGINT 需显式指定类型
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











