栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【超卖问题,高并发情况下,如何扣减库存】

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【超卖问题,高并发情况下,如何扣减库存】

目录

扣减库存需要注意的点方案一: 纯mysql扣减实现

原理实现优点缺点MYSQL架构升级读写分离再次升级代码实现: 方案二:缓存实现扣减方案三:数据库+缓存

顺序写的架构扣减流程 总结

扣减库存的操作节点

下单减库存付款减库存预扣减库存防范恶意用户小结
高并发场景下,商品展示页上面的信息,除了库存的其他信息属于静态数据,静态数据是可以缓存的。动态数据只有库存。
电商项目对并发数据处理要求较高。

扣减库存需要注意的点

剩余库存要大于等于当前需要扣减的库存,不允许超卖对于同一个商品,用户并发扣减时,要保证并发的一致性保证可用性和性能,性能至少是秒级一次扣减包含多个商品扣减多个商品时,一个不成功则全部不成功,需要回滚下单时必须产生了扣减,退款时才能归还,归还的数量必须加回去,不能丢失下单时的一次扣减,可以多次归还归还时需要保证幂等 方案一: 纯mysql扣减实现

扣减业务完全依赖MYSQL数据库来实现,不依赖中间件或缓存。

原理

基于数据库乐观锁方式保证并发扣减的强一致性基于数据库的事务实现批量扣减失败进行回滚 实现

流程图

一次完整的流程就是先进行数据校验,做接口开发的时候,要保持一个不信任原则,一切数据都不要相信,都要做校验判断,其次还可以进行库存扣减的前置校验,如果库存只有8个,用户要买10个,此时的数据校验中,可以拦截,减少对数据库的写操作。纯读不会加锁,性能较高。关键sql

update xxx set 库存 = 库存-10 where skuid = 'xxx' and 库存>= 10 

用户每次扣减的时候,需要传入一个uuid作为流水号,全局唯一:

当用户退单时,传回此编号,用来标识属于历史上的哪次扣减进行幂等控制,用户调用扣款接口时,出现超时,不知道成功了没,可以通过此编号进行重试或反查,重试时可通过此标识防重 优点

逻辑简单,开发部署成本低。

缺点

无法支持高并发,单机数据库并发1000,2000压力就非常大了,如果AB两个用户同时购买同一个商品,校验通过,后续购买时,只会有一个人成功,导致另外一个人失败,数据库也就多了一次查询,降低性能

MYSQL架构升级

根据场景分析,读库操作一般是浏览商品时产生,扣减库存是在购买时产生,用户购买请求的业务价值大,要保障写操作。

读写分离


根据二八原则,80%为读流量,主库压力降低了80%,但采用读写分离会导致读取的数据不准确,不过库存本身就在变,短暂差异,业务上可以允许,最终的扣减会保证数据的准确性。

再次升级

初次升级支持并发并不太高,我们可以引入缓存

加缓存reids,高并发,单机redis每秒支持并发可在3,4W

代码实现:

version做控制之类的,其实用不上,我们只需要
update where id and 库存>0.
下单失败了,给你返回执行的行数就是0。
if==0
return 下单失败
else
下单成功

方案二:缓存实现扣减

和前面的扣减库存其实一样,这里依赖redis,不依赖数据库。redis的hash结构不支持多个key批量操作,我们可采用redis+lua脚本来实现批量扣减单线程。

升级成纯redis实现扣减也会有问题

redis挂了,如果还没执行到redis扣减挂了,直接返回前端失败; 如果执行到redis扣减后,挂了,接口返回的失败,redis扣减成功了,但是没有触发异步更新逻辑,数据库不会扣减,数据库是准确的,这个时候需要一个对账程序,通过对比redis和数据库库存是否一致,并结合扣减日志表,发现扣减失败了,将数据库库存比redis多的库存加回到redis中。redis扣减完成,异步刷新数据库失败了,redis此时是准的,数据库库存是多的,结合扣减日志,将数据库比redis多的库存数据在数据库中进行扣减。 方案三:数据库+缓存

在磁盘写数据时,向文件末尾不断追加写入的性能远大于随机修改。对于传统机械硬盘来说,每次随机更新都需要磁头寻址,向文件末尾追加数据,只需要寻址一次。
对固态硬盘来说,虽然避免了磁头移动,但依然存在寻址过程。对文件的随机更新和数据库表更新比较类似,都存在加锁带来的性能消耗。

顺序写的架构

与纯缓存架构的区别是,写入任务数据库不是异步,而是在扣减的时候同步写入,用的是顺序写,不是update做数据库数量的更改,所以性能更好。
insert任务数据库,只记录操作,不进行真实扣减。

扣减流程

insert是顺序写,将update异步化,所以可以很大提高并发,这样会用到数据库事务来进行redis中的数据修改,所以不会出问题,不会出现少卖的问题。

总结

可以用方案1,但是后期业务量上来了,可以考虑后面用方案2,方案3。
大部分电商目前是基于方案2。

扣减库存的操作节点

扣减库存的节点分为

下单减库存付款减库存预扣库存 下单减库存

用户下单了,未必会付款

付款减库存

用户明明购买成功了,却不能付款。

预扣减库存

用户下单后,为用户预留库存,占用数量就是购买的数量,例如预留10分钟,超过10分钟释放用户库存,其他用户继续下单。
用户下单预扣减库存后,在付款时检查是否存在有效的预留库存,如果存在则扣减库存并付款。如果不存在则再次尝试预扣减库存,如果库存不足,则不付款。如果预扣减成功则真正扣减库存并付款。

防范恶意用户

经常下单不付款的用户打标签,这些用户特殊处理,不扣减库存等秒杀期间,设置同一个人的最大购买数不付款重复下单的用户进行限制,如果存在未付款的订单,并且是同一商品,提示用户先付款再提交订单 小结

大部分秒杀系统会采用下单减库存的方式。

扣减库存时在程序中判断库存是否为负,如果变为负数,回滚事务不再扣减库存数据库设置库存字段为无符号整数,从数据库层面保证无法出现负数。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/785220.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号