You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring JPA中如何同步更新实体 解决并发更新库存结果不符问题

问题根因

这是典型的丢失更新问题,本质是并发场景下读写操作没有做互斥控制,后提交的请求会把先提交请求的修改结果覆盖,最终值和预期不符。

你当前的库存操作默认执行逻辑是「先查询当前库存→应用层计算新值→将新值写入数据库」,并发调用加减接口时,两个请求会几乎同时读取到同一个旧库存值,各自完成计算后先后写入数据库,自然会出现结果不符合预期的情况。

举个简单的示例,初始库存为10:

  • 减库存接口读到库存10,计算得到新值9
  • 加库存接口同时读到库存10,计算得到新值11
  • 加库存接口先写入11,减库存接口后写入9,最终库存为9,和预期的10完全不符。
可行解决方案

1. 数据库原子更新(最推荐,实现成本最低)

不要把库存值读取到应用层做计算,直接在SQL层面完成增量操作,数据库本身的行锁会保证同一条商品记录的更新操作串行执行,完全避免丢失更新问题,SQL示例如下:

  • 扣减库存:UPDATE product SET quantity = quantity - 1 WHERE id = [商品ID]
  • 增加库存:UPDATE product SET quantity = quantity + 1 WHERE id = [商品ID]

如果需要做库存不足的校验,也可以直接在SQL里加条件,避免扣减时库存为负:UPDATE product SET quantity = quantity - 1 WHERE id = [商品ID] AND quantity >= 1,根据返回的影响行数判断是否扣减成功即可。

2. 加锁控制

如果你的业务逻辑必须要先把库存读取到应用层做复杂计算/校验,再执行关联操作,可以通过加锁实现互斥:

  • 单机部署场景可以直接用JVM进程内锁,以商品ID作为锁标识,请求拿到锁之后再执行完整的查库存→计算→写库存逻辑,执行完成后释放锁
  • 分布式部署场景可以用Redis分布式锁,同样以商品ID作为锁key,加锁成功后再执行业务逻辑

3. 乐观锁实现

给product表新增一个版本号字段version,每次更新时携带版本号做条件判断:

  1. 先查询当前库存和对应版本号:SELECT quantity, version FROM product WHERE id = [商品ID]
  2. 应用层计算新的库存值
  3. 执行更新SQL:UPDATE product SET quantity = [新库存值], version = version + 1 WHERE id = [商品ID] AND version = [之前查询到的版本号]
  4. 如果返回的更新行数为0,说明当前商品库存已经被其他请求修改过,可以根据业务需要做重试或者直接返回操作失败

内容的提问来源于stack exchange,提问作者Nguyễn Minh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 07:45:05