高并发下扣减库存
需求
系统中有商品,商品有库存数量,用户买商品扣减商品的库存。
扣减库存的几种方式
下单扣减
下单后立马扣减。有的人下单后不付款,恶意的人直接把库存下单完,影响商品销售。需要结合安全和反作弊的措施。
比如:
给经常下单不付款的买家进行标识(不扣减付款后再扣减)
某此类目最大购买数量
下单不付款的操作进行限制
付款扣减
付款后再扣减。影响用户体验,用户下单后以为成功了,会出现下单后付不了款。
超卖现象。超卖之后补货。
如果不允许超卖,会出现付款后扣减失败,退款流程,进一步影响用户体验。
预扣库存
用户下单后,扣减库存为其保留一定的时间(如10分钟),超过这个时间后,订单失效,这时候其他用户下单就可以购买,付款后扣减库存生效。
同样会有下单扣减同样的问题,恶意的人直接把库存下单完,影响商品销售。
秒杀场景
秒杀场景一般都是抢到就是赚到,所以成功下单后却不付款的情况比较少,再加上卖家对秒杀商品的库存有严格限制,所以秒杀商品采下单扣减更加合理。
下单扣减比预扣库存以及涉及第三方支付的付款扣减更为简单,所以性能上更占优势。
技术上方案
1 |
|
单体架构
方法一
使用synchronized或者lock
1 |
|
会出现超卖情况
方法二
1 |
|
存在ABA问题,允许后台增加库存,后台用户看到剩10个了,购买用户A购买了5个并成功后,后台用户增加10个并成功后,购买用户B购买了5个并成功,这时后台用户看商品数量还是10个。
方法三
1 |
|
不会出现ABA问题,但是会出现失败的情况。
集群架构
单体架构的方法一将失效。单体架构的方法二、方法三还是可以使用。方法二、三本质上是把并发交给mysql, 一般到了集群,mysql的并发也不能承受。
redis扣减
先看redis中是否有该商品的库存,没有就加载进redis,如果有就减库存。
redis处理逻辑的模块为单线程。
setnx 命令的函义为指定的 key 不存在时,为 key 设置指定的值。
1 |
|
增加了redis后,就会出现redis与mysql一致性的问题。可以看《如何保证数据库与缓存的数据一致性》文章。
这种增加缓存组件的场景是减库存逻辑非常单一,比如没有复杂的SKU和总库存这种联动关系的情况。
分布式锁
使用分布式锁,增加减少库存数量都加分布式锁。
常见的分布式锁有zookeeper、redis锁。