前言
最近在梳理后端业务场景时,发现商品库存扣减逻辑的实现存在一定的风险。所以,针对目前库存扣减实现方案做一些分析,然后再分享一些目前流行的解决方案。
当前实现方案
库存扣减-NM系统的实现方案.png
实现方式
只考虑更新数据库,缓存数据不考虑
执行步骤
1.订单服务发起扣减库存请求
2.库存服务执行库存减一 (使用DB CAS防止超卖)
3.商品详情中库存数据过期,重新获取库存数据
存在问题
1.出现库存数据不一致 (大概率)
2.并发性能低
3.库存数据应该缓存在库存服务中
适用场景
小系统(我们这种)
改良方案-Cache Aside
库存扣减-Cache Aside.png
实现方式
先更新数据库,再删除缓存
执行步骤
订单服务
1.发起扣减库存请求
库存服务
2.获取分布式锁
3.操作数据库,扣减库存
4.请求删除库存缓存
5.释放分布式锁
商品服务
6.获取最新的库存数据
存在问题
1.缓存删除失败,导致数据不一致 (小概率)
2.库存数据应该缓存在库存服务中
适用场景
并发小,可接受小概率数据不一致
疑问
为什么是删除缓存,不是更新缓存?
因为删除更简单,直接; 更新缓存有可能还涉及复杂的处理逻辑。
改良方案-Cache 双删
库存扣减-Cache双删.png
实现方式
先删除缓存==>操作数据库==>再删除缓存
执行步骤
订单服务
1.发起扣减库存请求
库存服务
2.获取分布式锁
3.删除库存缓存
4.操作数据库,扣减库存
5.删除库存缓存
6.释放分布式锁
商品服务
7.获取最新的库存数据
存在问题
1.高并行下,更新数据库未完成,同时有查询操作;这样在步骤4和步骤5间隔时间中就会出现数据不一致 (极小概率)
适用场景
高并发场景
改良方案-同步队列
库存扣减-内部队列.png
实现方式
在库存服务中实现一个同步队列,所有操作通过队列来实现串行执行
执行步骤
订单服务
1.发起扣减库存请求
库存服务
2.扣减请求放入队列中
3.从队列中获取一个操作
3.执行删除库存缓存
4.操作数据库,扣减库存
5.操作完成
商品服务
7.获取最新的库存数据 (等待库存更新完成后才能返回数据)
存在问题
实现复杂,需要考虑如何避免队列积压导致的读操作阻塞问题
适用场景
高并发场景