秒杀如何防止库存超卖?

考查重点:原子扣减与购买约束。原子条件扣权威库存并事务建单,重试/消费幂等,购买唯一约束兜底,不能先查再无条件扣

本题目录

参考答案

核心回答

防止秒杀超卖,关键是在权威库存中原子地完成“检查足够并扣减”,让扣库存与创建订单保持事务一致,并防止重试和消息重复造成多次扣减。仅仅先查库存大于零、随后再减一,会产生并发竞争。

以 MySQL 8.4 InnoDB 管理最终库存和订单为例:

  1. 扣减时在更新条件中要求库存不少于本次购买量,并检查实际影响行数;扣减失败就不能创建成功订单。也可以在事务内先锁定库存行,再检查与扣减。
  2. 扣库存、订单或预占记录在同一事务内提交,失败一起回滚。请求使用稳定业务标识,避免超时重试再次扣减。
  3. 流量大时,在入口限流,用 Redis Lua 原子预扣减少无效竞争,通过 MQ 异步处理订单;数据库仍保留最终约束。
  4. 对未支付超时、取消和失败建立状态机,按预占记录释放一次,处理重复消费、迟到消息和异常恢复。

Redis 与 MQ 是可选的承压手段,不能替代最终库存约束;一人一单则是另一条用户资格约束,需要相应业务唯一键和状态规则。

只剩一件时,数据库怎样避免竞态

如果两个请求都普通查询到库存为 1,再各自按这个旧值更新,就可能同时认为自己抢到商品。应把“库存足够”放进实际扣减的原子更新条件,或者在持有库存行锁的同一事务里完成检查和修改。

这样,一个事务成功扣完后,另一个请求执行扣减时必须根据受保护的当前状态判断,不能继续用之前查询到的 1。应用检查更新结果,只有扣减成功才推进订单;事务中途失败,则连同订单相关写入一起回滚。购买数量也要验证为合法正数,不能让负数购买反向增加库存。

如果使用版本号做乐观控制,也要同时验证库存足够,并处理更新失败;高竞争下无限重试只会继续放大负载。库存行的定位应有合适索引,事务尽量短,避免持锁期间远程调用支付等服务。

Redis 预扣和 MQ 各负责什么

Redis 中的检查与预扣应放在同一个短 Lua 脚本,避免应用把读取和修改拆成多次独立命令。MQ 把突发请求转为可控的后台处理速率,但队列有容量和保留边界,不能无限接收。

Redis 预扣成功不等于数据库订单已成功。预扣后进程崩溃、发送消息超时、消息重复、Redis 状态在故障切换中丢失,都需要恢复协议。应有可恢复的请求与待处理记录,按稳定标识重试投递和查询结果;发送超时可能只是确认没到,不能立即无条件补库存。

数据库扣减与订单事务仍是最终检查:即使 Redis 误放行,也不能突破最终额度。相应代价是预占被受理后仍可能失败,需要异步结果查询、失败通知与预占清理,不能在入口直接承诺最终成功。

重试、取消和多渠道库存怎样处理

同一订单消息可能在数据库提交后因确认失败再次投递,应识别已有业务结果,不能再扣一遍。数据库唯一约束可以防止重复订单,约束记录与扣减要在同一事务中;仅给请求换一个新 ID,就不能再视为同一次重试。

取消释放必须是有条件、一次性的状态转换:只有确实预占且尚未成功消耗或释放的额度才能返还。已支付订单不能被迟到的超时任务取消,重复取消也不能重复加库存。支付、取消和释放要协调各自允许的状态迁移。

如果多个销售入口、后台人工修改或多个活动共享同一批库存,它们必须遵守同一个库存规则;独立 Redis 计数器各自都不减成负数,也不代表总销量不会超额。需要分桶时,应先分配总和不超过可售库存的额度,并管理额度转移,不能凭空给每个桶一份完整库存。活动开始和恢复时还要对账预占、订单与可售量,发现异常及时限制继续受理。

参考资料

  • 国内:JavaGuide:服务限流详解:用于解释入口承压与 Redis Lua 原子检查的作用,区分限流、预扣和最终库存安全。
  • 国内:JavaGuide:接口幂等性设计详解:用于设计稳定业务标识、唯一约束及同事务的去重和扣减,防止重复订单和重复释放。
  • 国内:阿里云:消息幂等:用于解释发送与确认失败后的重投,说明后台订单处理为什么仍需业务幂等。
  • 国外:Redis:Scripting with Lua:用于核对脚本内条件检查和更新的原子执行,以及它不能覆盖其他系统事务的范围。
  • 国外:MySQL 8.4:Locking Reads:用于解释共享计数记录为何不能先普通读再更新,以及锁定读取、更新和事务释放的关系。

相关面经

8 篇

百度

Java开发
社招一面
查看原面经 →
秒杀场景如何避免超卖(不用redis)?

恒生电子

后端开发(Java)
秋招一面
查看原面经 →
高并发本地生活项目防超卖的整体链路

小红书

面经合集
查看原面经 →
电商有个热点福利品,库存几十万,会触发秒杀,你作为秒杀链路负责人,怎么设计优化整条链路保证高TPS?

科大讯飞、首都在线、宇量晟、四川长虹、度小满、字节跳动、恒生电子、腾娱互动、金蝶、汽车之家

面经合集
查看原面经 →
面试官真是我实习面试,秋招面试以来最耐心,最和蔼的一个了,问的非结构化问题比较多,问了一些项目。重点围绕出现问题怎么解决,怎么排查。场景题比较多:库存超卖,高并发场景限流。面试官在我答完后会发表自己的见解,整场面试下来感觉是在和一个前辈交流,而不是被人拷打,夯爆了。当场给过了。

水滴

质量保障工程师(AI)
校招秋招一面
查看原面经 →
数据库表中的一行是某商品的库存,只剩1了,有十多个人在抢这个货,会出现什么样的问题,如何去解决这个问题。
展开剩余 3 篇面经

MemTensor

全栈开发工程师
社招一面
查看原面经 →
抢票/秒杀场景的设计思路(分桶、削峰、Redis原子操作)。

美团

后端开发工程师
秋招一面
查看原面经 →
秒杀防超卖功能是如何实现的。

深势科技

后端开发工程师(AI / Agent 方向)
二面
查看原面经 →
防止超卖是怎么做的?

相关题目

4 题