我是靠谱客的博主 彩色舞蹈,最近开发中收集的这篇文章主要介绍高并发下的库存扣减方案,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

高并发下的库存扣减方案

背景

直接进入主题:如果老板让你设计一套高并发下的库存扣减方案,不能出现超买超卖。你是否有相似的工作经验?是否有方案的设计思路?近些年在营销项目组的工作经验让我对【库存扣减】的方案有了些许认知,接下来的文章,带着大家感受下从0-1的库存扣减方案的的诞生,欢迎大家的指导!
在这里插入图片描述

那年还很low(DB)

刚开始我们的营销项目组身单力薄,人微言轻;那时营销业务才刚开始发展,此时我们把业务放到第一位,技术方案为满足时间内业务发展所让步。大家应该可以猜到,这个时候我们很low的库存扣减方案-直接上数据库。根据业务诉求,每个活动会保存一份实时变化的库存,参与活动时,实时扣减数据库,操作步骤如下:

  • ①接收业务扣减库存请求
  • ②数据库查询单个活动库存,并对查询的单条数据加锁(select * from 库存表 for update)
  • ③验证库存是否满足扣减数量,满足则扣除

在这里插入图片描述

线上遇到了问题(DB锁优化)

可想而知,好景不长,当线上业务逐渐增长,数据库出现了瓶颈。表现主要是数据库性能低,大量的资源处于等待锁的状态,影响线上业务,影响用户体验。当然这里主要原因还是活动库存记录为热点数据,当收到大量扣减库存请求时,针对同一个活动的库存扣减会因为数据库行锁导致大量线程等待。

此时为了即可解决生产出现的数据库锁问题,临时方案为:调整上方步骤的第②步,由【select * from 库存表 for update】 调整为【select * from 库存表 for update nowait】,由之前的数据库等待锁改成了非等待锁。也就是说当有线程1正在查询且更新活动A库存时,并发情况下,如果同一时间线程2也需要更新活动A库存则会提示资源忙且退出操作。
当然这个牺牲了一定的用户体验;完全不能用于秒杀类活动。

在这里插入图片描述

先扛着(DB异步+同步)

临时解决了生产问题之后,我们着手调整库存扣减方案,希望在准确性和用户体验上找到一个更好的方案。于是有了这个数据库的升级版方案。此方案还是主要采用数据库锁+异步扣减库存,步骤如下:

  • 活动配置时,首先初始化活动库存阀值(到达阀值后库存扣减由异步转为同步)
  • 接收扣减库存的请求,[同步]或者[异步]扣减库存
  • 业务成功入库存扣减请求表(同步-核算,异步-未核算)
  • 定时任务异步更新库存,如果库存低于阀值,则修改活动库存状态为同步扣减

当然此方案也有弊端:由于活动形式多、优惠力度不同、权益样式多等特点,合理的设置阀值比较困难;异步转同步期间,可能会出现超买超卖;同步扣成本虽然与之前相比添加了重试次数,但对用户体验还是有很大的牺牲。
在这里插入图片描述

破斧沉舟(redis分布式缓存+redis分布式锁+异步)

以上种种方案,都是以数据库锁为基础做的优化,众所周知数据库资源是我们最宝贵且最容易成为短板的稀缺资源之一。所以我们必须要进一步改进,然后有了下面的方案…

首先我们对于数据库的[库存]依然采用异步处理的方式同步,同时使用能够满足高并发业务且基于内存的分布式缓存redis做活动库存的缓存。业务处理时,先以redis保存的已用库存为准做库存的check和扣减,当缓存丢失时,再实时同步已有库存到缓存,大概的步骤如下:

  • 活动申请时REDIS初始化活动【已用库存】,数值为0(redis作为分布式缓存)
  • 接受库存扣减请求,使用redis的INCR命令更新单个活动的已用库存数量(redis作为计数器)
  • 业务处理完成后,同一事务内入库主业务表、库存扣减请求表
  • task定时任务采用流式处理实时更新数据库库存,同时比对REDIS保存的已用库存,异常则告警。

REDIS作为一个高性能的非关系型key-value数据库,可作为分布式缓存、计数器、分布式锁、分布式队列等。是一种能够很好解决库存扣减的方式的技术之一。

在这里插入图片描述

结束语

有时候解决问题不仅靠技术的支持,也需要有合理的业务方案;技术+业务+当时利弊的权衡也许能设计出符合现状的合理的且可持续发展的方案。

正如CAP定理,在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance),这三个要素最多只能同时实现两点,不可能三者兼顾。

附录(思考)

  • Redis缓存丢失之后同步缓存是否有问题?有什么解决方案?
  • 库存扣减主逻辑中先查询redis缓存,存在则扣减是否有问题?有什么解决方案?
  • 普通活动和热点活动的发现以及库存扣减隔离…

答案:

  • 关于以上我们的最终方案(redis+异步),其实是以用户体验优先,并不是强一致性。当redis因为服务重启、缓存淘汰等某些原因key丢失时,也有可能会因为同步redis缓存和数据insert库存扣减表并发执行,导致redis缓存数据不准确,需要靠后期定时任务比对矫正才能保证最终一致性。
  • 查询存在,但恰好更新之前丢失,导致redis缓存库存异常,可以用redis+luna脚本保证redis操作的原子性(redis本身的事务不想我们平时用的spring事务支持异常回滚)

最后

以上就是彩色舞蹈为你收集整理的高并发下的库存扣减方案的全部内容,希望文章能够帮你解决高并发下的库存扣减方案所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部