秒杀漏斗模型该如何设计呢

1 阅读12分钟

秒杀漏斗模型该如何设计呢

做秒杀系统时,很多人第一反应都是:

  • Redis 扛库存
  • MQ 削峰
  • 限流
  • 异步下单

这些都没错,但如果只是零散地堆技术点,最后很容易出现一个问题:

每个点都做了优化,但整个链路还是扛不住。

原因通常不是你少用了某个中间件,而是你没有从系统整体视角去看秒杀。

真实的秒杀系统,核心不是“让所有请求都进来再处理”,而是:

让请求像漏斗一样,一层一层被筛掉,只让真正有机会成交的流量继续往后走。

这就是秒杀漏斗模型。

这篇文章就从工程实践角度,讲清楚:

  1. 什么是秒杀漏斗模型
  2. 为什么秒杀一定要按漏斗来设计
  3. 每一层漏斗分别解决什么问题
  4. 一套更接近真实生产环境的秒杀链路怎么落地

一、什么是秒杀漏斗模型

所谓秒杀漏斗模型,本质上是:

把秒杀请求从入口到最终成交,设计成一个逐层收口、逐层过滤、逐层削峰的处理过程。

你可以把它理解成一个漏斗:

  • 漏斗上层:海量请求涌入
  • 漏斗中层:大量无效请求被拦截
  • 漏斗下层:只有少量高价值请求真正进入下单、支付、履约流程

也就是说,秒杀系统不是追求:

所有请求都处理成功

而是追求:

在有限资源下,让最有可能成交的那一小部分请求稳定完成。

这是秒杀系统和普通电商系统最大的区别之一。

二、为什么秒杀一定要做成漏斗

先看一个简单事实。

假设你一场秒杀活动:

  • 100 万用户同时抢
  • 库存只有 1000 件

这意味着:

  • 99.9% 的请求从业务结果上注定不会成功

如果你还让这 100 万请求都完整走一遍:

  • 登录校验
  • 商品查询
  • 库存判断
  • 下单事务
  • 数据库写入
  • MQ 投递

那系统一定扛不住。

所以秒杀的关键不是“处理更多请求”,而是:

尽早识别无效流量,并尽早把它挡在更便宜的层。

这就是漏斗思维的核心:

  • 越靠前的层,处理成本越低
  • 越靠后的层,处理能力越稀缺
  • 越早过滤,整体系统越稳

如果没有漏斗模型,常见后果就是:

  • Nginx 被打满
  • 应用线程耗尽
  • Redis 热点 key 被打爆
  • 数据库连接池耗尽
  • MQ 积压
  • 支付和履约链路也被拖垮

三、秒杀漏斗模型的核心思想

秒杀漏斗不是某一个组件,而是一种系统设计方法。

它强调三件事:

1. 越靠前越便宜

最前面的过滤一定要最轻量。

例如:

  • CDN 静态化
  • 网关限流
  • 验证码
  • 活动令牌

这些动作比你进入业务服务、访问 Redis、访问数据库都便宜得多。

2. 越靠后越稀缺

真正昂贵的资源包括:

  • Redis 热点库存 key
  • 下单服务线程池
  • MySQL 事务
  • MQ 消费能力
  • 支付和履约系统处理能力

这些资源不能被无效请求浪费。

3. 每一层都要收口

漏斗不是只在入口限一次流就结束了,而是每层都要继续做过滤。

例如:

  • 网关挡一批
  • 资格校验挡一批
  • 令牌机制挡一批
  • 预扣库存挡一批
  • 下单幂等再挡一批
  • 支付超时再筛掉一批

最后真正完成支付的,才是最终成交用户。

四、秒杀漏斗可以怎么分层

从真实系统角度,一个比较常见的秒杀漏斗大致可以拆成下面几层。

第一层:流量入口层

这一层面对的是最原始、最粗暴的请求洪峰。

主要目标是:

  • 拦截机器流量
  • 限制异常请求
  • 降低源站压力

常见手段:

  • CDN 缓存静态页面
  • 前端静态化
  • 黑白名单
  • IP 限流
  • 用户维度限流
  • 验证码
  • 动态路径

为什么这一层很重要?

因为如果连最外层都不拦,后面所有层都会被无效流量拖死。

比如:

  • 活动页面、商品详情页尽量走静态资源
  • 秒杀按钮请求前先做人机校验
  • 同一用户短时间多次点击直接拦截

这一层的目标不是保证业务正确,而是:

先挡住最便宜也最没价值的流量。

第二层:活动资格层

不是所有用户都有资格参与秒杀。

例如:

  • 用户未登录
  • 用户未实名认证
  • 用户不在活动范围
  • 用户不属于会员等级
  • 用户已经抢过一次

这类请求应该在真正抢库存前就被过滤掉。

常见做法:

  • 用户资格预计算
  • 活动白名单
  • 用户标签缓存
  • 抢购资格令牌

很多系统做得不够好的地方在于:

  • 用户一点击就去抢库存

这样会让大量根本没资格的人也去竞争库存资源,白白消耗 Redis 和业务线程。

第三层:请求令牌层

这是秒杀里非常实用的一层。

核心思路是:

不是所有请求都能直接进下单逻辑,必须先拿到令牌。

令牌的作用包括:

  • 限制进入核心链路的总量
  • 做用户级防刷
  • 把无效点击挡在更前面

例如:

  • 系统按库存量和放大系数发放令牌
  • 用户拿到令牌后,才允许尝试秒杀
  • 没拿到令牌,直接失败

令牌比真正下单更轻量,因为它可以只依赖:

  • Redis
  • 内存计数器
  • 活动状态缓存

而不需要直接走数据库事务。

这一层的本质是:

先控制进入核心资源区的人数。

第四层:库存校验层

到了这一层,才是真正意义上的资源竞争。

秒杀库存一般不会直接用数据库实时扣减,而是先做缓存层预扣。

常见方案:

  • Redis 原子扣减
  • Lua 脚本扣库存
  • 本地标记售罄

为什么不能一上来打数据库?

因为数据库事务扛不住这么高的并发争抢。

所以典型做法是:

  1. Redis 里预热库存
  2. 请求进来先在 Redis 扣减
  3. 扣减成功才有资格进入异步下单
  4. 扣减失败直接返回售罄

这一层的目标是:

把大量“抢不到库存”的请求快速终止,不让它们进入下单链路。

第五层:异步排队层

哪怕 Redis 预扣库存成功,也不能直接让所有请求同时打数据库下单。

这时就需要异步排队。

常见做法:

  • Kafka
  • RocketMQ
  • 本地队列加持久化兜底

流程一般是:

  1. 用户抢到库存资格
  2. 系统把下单请求写入 MQ
  3. 用户收到“排队中”
  4. 消费者异步创建订单

这样做的好处是:

  • 削峰填谷
  • 避免数据库瞬时被压垮
  • 下单服务可以按自身能力消费

这一层是漏斗里非常关键的一层,因为它把“瞬时洪峰”转成了“可控吞吐”。

第六层:订单创建层

到了这里,请求已经是“比较有希望成交”的流量了。

但即便如此,依然不能放松。

订单创建层通常还要继续做这些事情:

  • 幂等校验
  • 一人一单校验
  • 活动时间校验
  • 库存最终确认
  • 订单落库

为什么前面已经扣了库存,这里还要校验?

因为前面的库存扣减通常是缓存预扣,而最终业务成功还是要在数据库里落真实订单。

这里经常会配合:

  • 唯一索引控制一人一单
  • 本地事务保证订单写入
  • 消息表或事务消息保证后续一致性

这一层的目标是:

把“拿到资格的人”变成“真正成功的订单”。

第七层:支付转化层

很多人讲秒杀到订单创建就结束了,但真实业务里,秒杀最终是否成功,往往还要看支付。

因为会有大量用户:

  • 抢到了
  • 下单了
  • 但没付款

所以从业务结果上看,真正的秒杀漏斗底部,往往是“完成支付的人数”,而不是“创建订单的人数”。

这一层要处理的事情包括:

  • 支付时限控制
  • 订单超时关闭
  • 支付成功回调
  • 释放未支付库存

也就是说,秒杀库存并不是订单创建那一刻就万事大吉,很多系统还会结合:

  • 预占库存
  • 订单超时回补库存
  • 支付成功后最终锁定库存

这一层本质上是在做:

从抢购成功到最终成交的转化管理。

第八层:履约与结果反馈层

秒杀不是下完单就结束。

后面还包括:

  • 发货
  • 券核销
  • 库存最终出库
  • 用户结果通知

同时,用户侧还要快速知道结果:

  • 秒杀成功
  • 排队中
  • 已售罄
  • 下单成功待支付
  • 支付成功

所以这一层通常要做:

  • 结果异步通知
  • 订单状态查询缓存
  • 用户维度结果聚合

它的作用是:

让前面复杂的异步漏斗,对用户表现成一个清晰、稳定、可查询的结果。

五、为什么说漏斗模型的关键是“越早过滤越好”

这个观点非常重要。

秒杀系统里每往后走一步,成本都会明显上升。

大致可以这么理解:

  • 网关限流:成本最低
  • 活动资格校验:很低
  • Redis 令牌:低
  • Redis 扣库存:中
  • MQ 排队:中
  • MySQL 下单事务:高
  • 支付、履约、通知:更高

所以如果一个请求注定失败,你应该尽量让它死在前面:

  • 没资格,别进库存层
  • 没令牌,别进下单层
  • 没库存,别进 MQ
  • 没支付,别占用履约资源

如果过滤做得晚,后面昂贵资源就会被大量浪费。

这也是为什么秒杀系统的设计重点不是“后端足够强”,而是:

让后端只服务真正值得服务的请求。

六、漏斗模型下的几个核心技术手段

下面把秒杀系统里常见技术点,放回漏斗模型里看,就会更清楚。

1. 限流

位置:

  • 网关层
  • 用户层
  • 接口层

作用:

  • 防止系统被瞬时流量打穿

2. 验证码和动态路径

位置:

  • 活动入口层

作用:

  • 拦截脚本和机器流量

3. 令牌机制

位置:

  • 资格层之后、库存层之前

作用:

  • 控制进入核心链路的人数

4. Redis 预扣库存

位置:

  • 核心资源竞争层

作用:

  • 快速判定是否还有抢购机会

5. MQ 异步下单

位置:

  • 库存成功之后

作用:

  • 削峰填谷
  • 把同步压力变成异步可控吞吐

6. 幂等和唯一约束

位置:

  • 下单层
  • 支付层

作用:

  • 防止重复请求、重复下单、重复支付处理

7. 支付超时释放

位置:

  • 支付转化层

作用:

  • 回收未付款订单占用的资源

七、一个真实的秒杀漏斗链路

如果把整条链路串起来,可以理解成这样:

  1. 用户访问秒杀页,页面静态化,CDN 抗住大部分流量
  2. 用户点击抢购,先经过验证码、限流、动态路径校验
  3. 校验用户活动资格,没有资格直接失败
  4. 用户尝试获取秒杀令牌,没有令牌直接失败
  5. 令牌通过后,去 Redis 做库存预扣
  6. 预扣成功后写入 MQ,返回“排队中”
  7. 消费者异步创建订单,做幂等和一人一单校验
  8. 订单创建成功后进入待支付状态
  9. 用户支付成功后订单最终成交
  10. 未支付订单在超时时间后关闭,并回补库存

你会发现,这其实就是一个典型漏斗:

  • 上层进来的人很多
  • 中间被一层层筛掉
  • 最后只有极少数人到达底部成交

八、秒杀漏斗模型的价值,不只是“扛高并发”

很多人理解秒杀漏斗,只看到“削峰限流”,其实它还有几个更重要的价值。

1. 保护核心资源

漏斗的本质是保护:

  • Redis 热点 key
  • 下单服务
  • 数据库
  • 支付系统

2. 提高整体成功率

如果系统不分层过滤,真正有机会成功的请求反而会被无效流量拖死。

3. 提高用户体验

用户不一定要求“必须抢到”,但一定要求:

  • 系统别崩
  • 结果别乱
  • 失败也要快

漏斗模型能让失败更早发生、更快返回。

4. 便于容量评估

每一层漏斗都可以单独评估转化率和承载能力。

例如:

  • 入口 100 万请求
  • 验证码后剩 60 万
  • 资格后剩 20 万
  • 令牌后剩 2 万
  • 库存成功 1000
  • 最终支付 800

这样你就能非常清楚地知道:

  • 哪一层拦截效果不好
  • 哪一层成本过高
  • 哪一层是瓶颈

九、最后总结

秒杀系统真正难的地方,从来都不是某一个技术点,而是:

如何让海量请求在有限资源下,有序地被过滤、排队、转化,最终只让极少数有效请求完成成交。

这就是秒杀漏斗模型的意义。

你可以把它记成一句话:

秒杀不是让所有流量都冲进来,而是让流量一层一层被筛选,越往后越值钱。

如果你理解了这个模型,再去看:

  • 限流
  • 验证码
  • 动态路径
  • 令牌机制
  • Redis 扣库存
  • MQ 削峰
  • 异步下单
  • 支付超时释放

这些技术点就不会是零散知识,而会变成一套完整的系统设计方法。