防止库存超卖
背景
在电商系统中买商品过程,先加入购物车,然后选中商品,点击结算,即会进入待支付状态,后续支付。 过程需要检验库存是否足够,保证库存不被超卖。
目的
- 防止相同用户重复下单
- 检查库存准确数量
- 防止扣错库存数量
- 扣库存时性能效率提升、不阻塞用户
一般过程
1.select根据商品id查询商品的库存。
2.根据下单的数量,计算库存是否足够,如果存库不足则抛出库存不足的异常,如果库存足够,则减去扣除的库存得到最新的库存剩余值。
3.set设置最新的库存剩余值。
显然,在多线程的情况下,可能会出现卖出商品的数量大于库存商品的数量,也就是电商中常说的 库存超卖。
解决方法
数据库
悲观锁
使用select···for update来将商品的库存锁住来避免库存超卖。
由于for update 会锁住所有扫描的数据,其他事务必须等这个事务执行结束再进行执行,所以性能比价低下
乐观锁
其实和java中常说的cas类似,比较然后替换,一般是在数据库中加一个version字段,每次修改的时候 看看自己拿到的版本号和数据中一样的不一样,一样就继续玩,不一样就放弃这个锁。
redis
使用redis原子操作+sql乐观锁
利用Redis decr的原子操作,保证库存数安全 先查询redis中是否有库存信息,如果没有就去数据库查,这样就可以减少访问数据库的次数。
- 获取到后把数值填入redis,以商品id为key,数量为value。
- 注意要设置序列化方式为StringRedisSerializer,不然不能把value做加减操作。
- 还需要设置redis对应这个key的超时时间,以防所有商品库存数据都在redis中。 update使用乐观锁
update Product set count = count - #{购买数量} where id = #{id} and count - #{购买数量} >= 0;
虽然redis已经防止了超卖,但是数据库层面,为了也要防止超卖,以防redis崩溃时无法使用或者不需要redis处理时,则用乐观锁,因为不一定全部商品都用redis。
LUA脚本保持库存原子性
分布式锁
参考
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果