如何设计一个秒杀系统
15
15
1.0x
00:00/00:00
登录|注册

06 | 秒杀系统“减库存”设计的核心逻辑

解决并发锁问题
数据库中减库存
缓存中减库存
极致优化思路
适用方案
库存超卖
恶意下单
预扣库存
付款减库存
下单减库存
极致优化思路
秒杀场景下的实现
可能的缓解办法
三种实现方案
大型秒杀中的减库存
可能存在的问题
减库存方式
总结
减库存设计的核心逻辑

该思维导图由 AI 生成,仅供参考

如果要设计一套秒杀系统,那我想你的老板肯定会先对你说:千万不要超卖,这是大前提。
如果你第一次接触秒杀,那你可能还不太理解,库存 100 件就卖 100 件,在数据库里减到 0 就好了啊,这有什么麻烦的?是的,理论上是这样,但是具体到业务场景中,“减库存”就不是这么简单了。
例如,我们平常购物都是这样,看到喜欢的商品然后下单,但并不是每个下单请求你都最后付款了。你说系统是用户下单了就算这个商品卖出去了,还是等到用户真正付款了才算卖出了呢?这的确是个问题!
我们可以先根据减库存是发生在下单阶段还是付款阶段,把减库存做一下划分。

减库存有哪几种方式

在正常的电商平台购物场景中,用户的实际购买过程一般分为两步:下单和付款。你想买一台 iPhone 手机,在商品页面点了“立即购买”按钮,核对信息之后点击“提交订单”,这一步称为下单操作。下单之后,你只有真正完成付款操作才能算真正购买,也就是俗话说的“落袋为安”。
那如果你是架构师,你会在哪个环节完成减库存的操作呢?总结来说,减库存操作一般有如下几个方式:
下单减库存,即当买家下单后,在商品的总库存中减去买家购买数量。下单减库存是最简单的减库存方式,也是控制最精确的一种,下单时直接通过数据库的事务机制控制商品库存,这样一定不会出现超卖的情况。但是你要知道,有些人下完单可能并不会付款。
付款减库存,即买家下单后,并不立即减库存,而是等到有用户付款后才真正减库存,否则库存一直保留给其他买家。但因为付款时才减库存,如果并发比较高,有可能出现买家下单后付不了款的情况,因为可能商品已经被其他人买走了。
预扣库存,这种方式相对复杂一些,买家下单后,库存为其保留一定的时间(如 10 分钟),超过这个时间,库存将会自动释放,释放后其他买家就可以继续购买。在买家付款前,系统会校验该订单的库存是否还有保留:如果没有保留,则再次尝试预扣;如果库存不足(也就是预扣失败)则不允许继续付款;如果预扣成功,则完成付款并实际地减去库存。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

文章总结:秒杀系统中的减库存设计是核心逻辑之一,本文介绍了减库存的核心问题和几种方式,包括下单减库存、付款减库存和预扣库存,并分析了它们可能存在的问题。针对大型秒杀活动,建议采用“下单减库存”方式,因为成功下单后不付款的情况较少,且逻辑简单性能优势明显。在数据一致性上,需要保证库存数据不能为负数,可以通过事务、设置数据库字段为无符号整数或使用CASE WHEN判断语句来实现。文章强调了在实际应用中需要结合安全和反作弊措施来解决恶意下单和库存超卖等问题。此外,文章还探讨了秒杀场景下减库存的极致优化思路,包括在缓存中减库存和在数据库中减库存的方式。针对数据库并发锁的问题,提出了应用层和数据库层做排队的解决办法,并介绍了阿里的数据库团队开发的针对MySQL的InnoDB层上的补丁程序,以及数据更新操作的优化方法。总结来说,本文围绕商品减库存的场景,介绍了减库存的实现方案、存在的问题和缓解办法,以及在秒杀场景下的极致优化思路,为读者提供了深入的技术内容。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《如何设计一个秒杀系统》
立即购买
登录 后留言

全部留言(108)

  • 最新
  • 精选
  • 周龙亭
    下单和扣库存两个操作的事务性是怎么做的?

    作者回复: 可以分两步来做,先创建订单但是先不生效,然后减库存,如果减库存成功后再生效订单,否则订单不生效

    2018-10-07
    9
    32
  • 刘小刘
    老师,我觉得你讲的不太明白,你并没有说实际情况下同步是怎样解决并发的,没看到您给的方案,只看到您在评论回复里否定了队列异步处理的方式

    作者回复: 解决的办法就是尽可能避免产生锁,比如根据商品ID进行分库分表设计;再有就是减少锁的粒度例如阿里对MySQL做了定制优化,可以提升MySQL的并发度

    2018-12-21
    2
    20
  • Geek_c19c96
    我们的库存都放在redis里面,读和减库存都在redis里面操作,redis会定时将库存放到mysql中做备份,

    作者回复: 😉

    2018-10-16
    10
    11
  • 公号-技术夜未眠
    许老师好,我有一个想法,只是没有在实践中这样做过,请指教: 能否借用"数据库水平拆分"的思想? 具体思路如下: 库存在数据库的表中就只有一行数据,上面的方案都是对这一条记录进行频繁更新,是非常"热"的热点数据。我们能否将该行数据拆分到不同的数据库中,这些数据库的库存记录之和就是原始库存数量。这样能否会降低数据库的写压力,提高吞吐量?

    作者回复: 实际上,商品都是进行分库分表的,例如根据商品id进行水平拆分 分库分表就是提高吞吐量

    2018-10-06
    4
    11
  • 我是李香兰小朋友
    “按照商品维度设置队列顺序执行”这句话是什么意思?可以举例说明一下吗?谢谢老师

    作者回复: “按照商品维度设置队列顺序执行”的意思就是,为了防止同一个商品对数据库的操作占用太多的数据库资源,所以采用队列的方式,让其他商品也有公平的机会得到数据的响应,例如如果秒杀的时候,秒杀商品肯定占用大量的请求,数据库的连接池有可能都被秒杀商品占用了,如果不做队列的话,那么其他商品就得不到数据库执行机会了。加入我们分10个队列,那么秒杀商品就会落在这10个队列中的一个,那么最多也就占用机器10分之一的资源。

    2018-10-13
    10
  • Coder4
    这种无只能在串行隔离级别才能用吧,不然肯定超售。。。UPDATE item SET inventory = CASE WHEN inventory >= xxx THEN inventory-xxx ELSE inventory END

    作者回复: 数据库层不都是串行操作吗😊

    2018-10-06
    17
    10
  • 永光
    老师,你好, 你提到秒杀商品减库存直接放到缓存系统中实现,也就是直接在缓存中减库存或者在一个带有持久化功能的缓存系统(如 Redis)中完成。这种实现并发读写怎样保持数据一致?以及是不是要用分布式缓存?

    作者回复: 前面有个同学的类似的问题回答过,可以看一下

    2018-10-06
    10
  • shawn
    个人做法, 针对确定库存,提前下好单,下单人留空,订单短时间内失效 订单id压入Redis队列, 请求来到,订单队列lpop,队空则返回失败, pop出来的订单补充下单人为当前用户, 如果订单过期失效则再次下同一商品的空单存入队列 这个设计可以考虑单个Redis不够用的时候将队列分组,利用轮转或时间戳hash将请求分配到不同队列, 想问下老师,这个和扣数字库存相比,会不会有更好的并发性能呢?

    作者回复: 说实话,没看出来哪里性能会更好😄 不过提前下单的思路还比较新颖,你的思路我理解,但是这样就把一个事情分两次来做,会增加了复杂度,有可能导致得不偿失

    2018-10-14
    6
    9
  • 一笑奈何
    老师,问下单机mysql 1s内能抗大约多大的QPS? 大约。

    作者回复: 我印象中单实例一般能抗7-8k左右

    2018-10-06
    2
    9
  • 大麦
    秒杀是短时间大量请求,使用下单即锁库存方式,可以通过一个 redis 队列记录下单,一个redis key 记录数量 num,超出的库存下单失败,这样大量请求在 redis 层即可被处理。 通过 num 与库存的判断来解决无效订单。 下单端通过队列异步消费下单。 对于前端,用户下单成功,即进入redis 队列的,响应给前端可以轮询。 没有的,直接提示抢购失败。

    作者回复: 异步下单的方式,也是一个思路,例如在一些场景下其实已经在使用,例如一些支付场景中,付了款以后,前端页面中会有一个转圈,等个几秒钟再告诉你结果。 这种方式我个人觉得对用户不太友好,就是要让用户等个几秒钟,而不是像同步的方式能及时得到反馈结果

    2018-10-12
    3
    7
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部