D
高并发资深40 分钟

秒杀系统设计

10 万请求抢 100 件库存,先削峰,再防超卖,最后异步落库。

设计高并发一致性MQ网关限流预约资格Redis LuaMQ 削峰DB CAS补偿对账
Java 场景 PDF Ch03 / Ch26 · 秒杀系统与库存超卖
所属路线 ·高并发系统
01

面试官问题

真实问法

如果让你设计一个秒杀系统,10 万 QPS 同时涌入但库存只有 100 件,你怎么保证不超卖、数据库不被打挂、失败还能回滚?

这题考什么

考分层削峰、热点库存原子扣减、异步下单、最终一致、失败回滚与补偿对账。

正确建模思路

把秒杀拆成过滤、预扣、排队、落库、补偿五段。入口只放行有资格请求,Redis 原子预扣库存,MQ 把洪峰削平,DB 用 CAS 做最终扣减,失败路径回滚 Redis 并对账。

02

业务背景

秒杀活动流量集中、库存极少,系统要快速拒绝大部分请求,只让有资格且预扣成功的请求进入下单链路。

学完后你能回答
  • 秒杀为什么要分层削峰?
  • Redis Lua 如何防超卖和重复购买?
  • 预扣成功但落库失败如何补偿?
03

关键约束

  • 卖出数量不能超过库存。
  • 数据库不能承受入口洪峰。
  • 失败路径要能回滚预扣库存。
  • 用户要尽快知道抢购状态。
04

系统建模

  1. 1

    资格网关:活动窗口、预约、风控和限流。

  2. 2

    Redis 库存池:Lua 原子预扣库存和限购。

  3. 3

    MQ 队列:把瞬时洪峰削成可消费的稳定流量。

  4. 4

    DB 真相源:最终扣减库存并创建订单。

  5. 5

    补偿任务:修复 Redis、MQ、DB 之间的不一致。

05

动画推演

动画看什么

看请求粒子流如何被资格网关、风控和 Redis 库存池逐层削减;只有预扣成功的少量请求进入 MQ,消费者再平稳写 DB。

  1. 01 流量洪峰进入网关。
  2. 02 活动窗口 / 预约资格 / 风控过滤。
  3. 03 Redis 原子预扣库存。
  4. 04 MQ 异步削峰。
  5. 05 DB CAS 最终扣减。
  6. 06 失败回滚库存。
  7. 07 补偿任务修复不一致。
视觉元素
请求粒子流资格网关Redis 库存池MQ 队列DB 真相源补偿机器人

逐层观察流量从网关、Redis、MQ 到 DB 被削峰和校正的过程。

秒杀库存实验台
Redis 预扣
100

待命

资格网关
资格通过

资格校验通过

DB 库存
100

最终真相源

资格RedisMQDB CAS补偿
timeline4 events · 4 frames
预约 / 资格校验 · 1/4
06

故障注入 / 错误做法

错误回答

请求进来直接查数据库库存,库存大于 0 就扣减并创建订单。并发高就把数据库机器加多一点。

正确回答

把秒杀拆成过滤、预扣、排队、落库、补偿五段。入口只放行有资格请求,Redis 原子预扣库存,MQ 把洪峰削平,DB 用 CAS 做最终扣减,失败路径回滚 Redis 并对账。

故障注入

错误做法是所有请求同步扣 DB。动画会注入入口洪峰,展示数据库锁等待、连接池耗尽,以及 Redis 预扣 + MQ 后指标如何恢复。

风险点
  • Redis 扣了库存但 MQ 或 DB 下单失败。
  • 同一用户重复请求绕过限购。
  • 热点商品单 key 被打爆。
  • MQ 堆积导致用户长时间只看到排队中。
07

30 秒面试表达

  1. 01

    秒杀不是让所有请求打数据库,而是分层削峰。

  2. 02

    入口先做活动窗口、预约资格、风控和限流,把无效流量挡掉。

  3. 03

    真正防超卖放在 Redis,用 Lua 原子判断库存和用户限购,预扣成功才发 MQ。

  4. 04

    消费者异步下单,DB 用 update stock=stock-1 where stock>0 兜最终一致;失败就回滚 Redis 库存,并用补偿任务修复 Redis 和 DB 的差异。

08

常见追问

Redis 预扣成功但下单失败怎么办?

失败路径要补回 Redis 库存,并记录补偿流水;对账任务周期性比对 Redis、订单和 DB 库存,发现漂移再修正。

如何防止一个人抢多件?

资格校验和 Redis Lua 脚本里都带用户维度去重,常见是 userId+activityId 限购键,重复请求直接拒绝。

MQ 堆积后用户体验怎么办?

前端展示排队中并轮询结果;后台扩消费者、批量消费或限速回放,必要时临时降级新请求保护交易链路。

09

复盘卡片

  • 入口削峰负责保护系统,不负责最终一致。
  • Redis 预扣负责快速判库存,DB CAS 负责最终账。
  • 所有失败路径都要有库存回滚和对账补偿。