扣减库存方案设计

扣减库存步骤

  1. 查询库存
  2. 判断是否超出库存
  3. 开始扣减
  4. 付款

第三四步可以互换。

遇到的问题

  • 超卖现象

用户 A 和 B 成功下单,在支付时扣减库存,当前库存数为 10。因 A 和 B 查询库存时,都还有库存数,所以 A 和 B 都可以付款。

完整的扣减库存方案_java

然后 A 线程更新最终库存数 lastInventory = inventory - 1 = 9,

B 线程更新库存数 lastInventory = inventory - 1 = 9。

而实际最终的库存应是 8 才对,这样就出现库存超卖的情况,而发不出货。

  • 恶意买家下单
    恶意买家指短时间内大量下单,将库存用完的买家。如果扣减库存后,用户不付款,占用库存名额,但是也要避免某些用户真实需要大量订单。

解决方案

  • 使用乐观锁扣减库存
  • 利用SQL语句更新库存,防止库存为负数
UPDATE [库存表] SET 库存数 - 1 WHERE 库存数 - 1 > 0

update的锁可以参考:​​mysql update锁分析​​ 这篇文章了解一下。

  • 分布式锁
    分布式锁,根据商品编号加锁,去扣减库存。
  • 预设库存到redis

秒杀场景提前将库存预设到redis,然后通过redis去扣减,但是要确保原子性(利用lua脚本实现);

  • 采用下单减库存

优点:实时减库存,避免付款时因库存不足减库存的问题

缺点:下单扣减库存是会造成恶意买家购买不付款占用库存的问题存在;

  • 预减库存

优点:结合下单减库存的优点,实时减库存,且缓解恶意买家大量下单的问题,保留时间内未支付,则释放库存。

缺点:保留时间内,恶意买家大量下单将库存用完。并发量很高的时候,依然会出现下单数超过库存数。

  • 付款扣减库存

优点:防止恶意买家大量下单用光库存,避免下单减库存的缺点

缺点:下单页面显示的库存数可能不是最新的库存数,而库存数用完后,下单页面的库存数没有刷新,出现下单数超过库存数,若支付的订单数超过库存数,则会出现支付失败。

  • 秒杀扣减并定时人工释放库存

目前直播带货已经非常的火爆了,很多主播上单都是秒清库存的状态,但是也有很多人在直播间只下单不付款,以抖音为例,经常会看到主播让大家抓紧付款,否则就要释放了,由此可见抖音电商使用的就是预减库存,人工可以释放库存的方式。

  • 数据库悲观锁控制

为了在事务控制中,防止写覆盖,你会想到使用select for update的方式,将该商品的库存锁住,然后执行余下的操作。

完整的扣减库存方案_redis_02

使用悲观锁方式,如果并发情况比较高的时候,扣减库存的操作是串行操作,效率很低。

方案的思路就是:

  1. 先下单
  2. 扣减库存
  3. 如果没有付款,指定时间后自动释放库存
  4. 如果是秒杀商品,库存被恶意占用,商家可以人工去通知大家,并手动释放没付款的那些库存给没有抢到的人去抢购
  5. 如果有人恶意占大量库存,也可以设置一个限购数量

结论

各种方案都可以,但是超卖问题必须解决,后续可以根据场景,比如是秒杀,还是直播间,或者普通的购物方式,选择合适的方案。

参考文档:
​​​《扣减库存方案》​​​​《浅析扣减库存的方案设计》​​