秒杀和大促场景如何做流量控制与削峰?
简化版
秒杀和大促的核心问题是瞬时流量远大于真实业务容量,而且大量请求集中访问少数商品和库存资源。设计时不能让所有请求直接打到订单、库存和数据库,而要做分层拦截、缓存预热、限流削峰、排队异步、库存原子扣减、幂等防重和降级兜底。
常见链路是:前端静态化和按钮控制,CDN 承接静态流量,网关按用户和接口限流,服务层做热点商品保护,Redis 预扣库存,MQ 削峰创建订单,数据库最终落库。卖完后快速返回,避免无意义请求继续冲击系统。
详细版
秒杀系统不是单纯把服务器扩容就能解决,因为瓶颈往往在库存扣减、订单创建、热点商品 Key、数据库写入和下游支付通知。正确做法是让大部分无效流量尽早结束,让少量有资格的请求进入核心链路。
入口层可以做验证码、登录态校验、活动资格校验、用户维度限流、设备或 IP 风控。商品详情、活动页、库存展示尽量静态化和缓存化。真正到下单时,先在缓存中做原子库存预扣,成功后写入 MQ,由订单服务按可控速率消费创建订单。对用户维度要做幂等,保证同一用户同一活动不能重复抢购。
削峰的重点是把瞬间的大流量变成后端能处理的平稳流量。但排队不是越长越好,队列满了、库存没了、活动结束了都要快速失败。系统还要有监控和应急开关,比如关闭活动、调整限流阈值、切换降级页面、暂停非核心功能。
完整版教学
一、秒杀流量的本质特点
秒杀和普通电商下单最大的区别是流量集中、时间集中、资源集中。普通下单可能分散在大量商品和较长时间内,而秒杀会在某个时间点把大量用户同时引导到少数商品上。
它有几个典型特点:
- 瞬时峰值极高:活动开始前后几秒流量可能暴涨几十倍甚至更多。
- 热点极强:大部分请求集中访问同一个商品、同一个库存 Key、同一个活动页。
- 成功比例很低:库存可能只有几百件,请求却有几十万。
- 用户对延迟敏感:等待太久会造成体验和投诉。
- 重试明显:用户会反复点击,客户端也可能自动重试。
因此秒杀流量控制的核心不是“全部接住”,而是“让系统只处理值得处理、能够处理的请求”。
二、第一层是前端和静态资源削峰
秒杀活动页、商品图片、规则说明、倒计时等内容应该尽量静态化,通过 CDN 承接。不要让每个用户刷新活动页都访问后端接口。
前端还可以做一些轻量控制:
- 按钮置灰:活动未开始、已结束、已售罄时不允许提交。
- 防重复点击:提交后短时间内禁用按钮。
- 倒计时校准:避免用户本地时间不准导致提前请求。
- 静态兜底页:后端压力过高时展示排队或售罄提示。
前端控制不能作为安全边界,因为用户可以绕过页面直接请求接口。但它可以减少大量普通用户的无效请求,是整体削峰的第一道缓冲。
三、第二层是网关和风控拦截
进入后端之前,网关要先做粗粒度控制:
- 用户限流:同一用户单位时间只能提交有限次数。
- IP 或设备限流:限制异常来源,降低脚本攻击影响。
- 接口限流:下单接口和查询接口使用不同阈值。
- 活动资格校验:未登录、无资格、黑名单用户尽早拒绝。
- 请求合法性校验:签名、时间戳、防重放 Token 等。
这一层的目标是把明显无效和恶意流量挡在核心业务之前。越靠前拒绝,系统成本越低。尤其是秒杀下单接口,不能让所有请求直接进入订单服务。
四、第三层是热点数据预热和缓存保护
秒杀商品通常可以提前知道,因此可以提前做缓存预热:商品信息、活动规则、库存初始值、用户资格、限购规则等都可以放到 Redis 或本地缓存中。
读路径要尽量避免访问数据库:
用户请求商品信息
↓
CDN / 本地缓存 / Redis
↓
只有缓存缺失或后台刷新才访问数据库
热点缓存要注意失效策略。活动期间不应该让核心热点 Key 自然过期后大量请求同时回源。可以使用逻辑过期、后台刷新、互斥重建和本地缓存兜底。对于“已售罄”状态,也要快速缓存,让后续请求直接返回售罄。
五、第四层是库存预扣和原子控制
库存是秒杀链路中最敏感的资源。常见做法是把活动库存提前加载到 Redis,用户请求到来时先用原子操作预扣库存。只有预扣成功的请求才进入后续订单创建流程。
常见实现方式包括 Redis Lua 脚本、数据库条件更新、库存分段等。Redis Lua 可以把检查库存、扣减库存、记录用户购买状态放在一个原子脚本里执行,减少并发问题。
库存控制必须考虑:
- 不能超卖:扣减操作要原子化。
- 不能重复买:同一用户同一活动要有幂等和限购记录。
- 失败要补偿:订单创建失败时,预扣库存是否回滚要有明确策略。
- 售罄要快速传播:库存为 0 后,应尽快让入口层和缓存层知道。
如果库存很大且并发极高,可以做库存分段,把一个热点库存拆成多个桶,降低单 Key 竞争。但分段会增加一致性和汇总复杂度,需要按业务规模决定。
六、第五层是 MQ 削峰和异步订单
即使库存预扣成功,也不一定要同步创建完整订单。订单创建涉及数据库写入、优惠券、风控、支付单等操作,如果所有成功请求同时落库,数据库仍然可能被打爆。
更常见的方式是:
用户下单请求
↓
Redis 原子预扣库存成功
↓
写入 MQ
↓
订单服务按固定速率消费
↓
创建订单并通知用户结果
MQ 的作用是把瞬时流量摊平,让订单服务和数据库按可承受速率处理。这里要注意,MQ 不是无限容量的垃圾桶。队列长度、消费延迟、失败重试、死信队列都要监控。队列满了要快速失败或降级,不能让请求无限等待。
七、幂等、降级和应急开关很关键
秒杀场景中,用户重复点击、客户端重试、网络超时都很常见。必须用幂等机制保证同一用户同一活动只会产生一个有效结果。可以使用用户 ID + 活动 ID + 商品 ID 作为幂等键,也可以在 Redis 和数据库层增加唯一约束。
降级策略也要提前设计:
- 查询降级:返回缓存数据或静态页面。
- 下单降级:队列满时返回排队失败或稍后再试。
- 非核心功能降级:关闭推荐、评论、复杂营销计算。
- 售罄降级:库存耗尽后直接返回售罄,不再进入核心链路。
- 活动开关:异常时可以暂停活动或调整限流阈值。
秒杀系统最怕没有刹车。应急开关不是可选项,而是保护业务和系统的最后手段。
八、常见误区与追问
这道题要紧扣「秒杀流量控制」本身回答,不能把它混成泛泛的流量控制套话。面试官通常不是只听定义,而是看你能不能把适用场景、关键流程、失败边界和工程取舍串起来,尤其要说明限流位置、算法窗口、阈值来源、拒绝策略、退避降级和观测指标。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心结论 | 秒杀要在入口削峰、库存预热、令牌发放、队列排队和异步下单之间分层控制,避免请求直冲数据库 | 不要停在名词解释 |
| 流程机制 | 活动预热库存 -> 入口限流和验证码 -> 发放有限令牌 -> 请求进入队列 -> 库存原子扣减 -> 异步生成订单 | 要说清触发点、状态变化、确认点和失败兜底 |
| 工程取舍 | 库存 100 件却进来 100 万请求,入口最多只应让略高于库存的有效请求进入核心链路 | 限流保护系统稳定性,但会牺牲一部分请求成功率或响应实时性 |
秒杀流量控制 面试拆解:
1. 活动预热库存
2. 入口限流和验证码
3. 发放有限令牌
4. 请求进入队列
5. 库存原子扣减
6. 异步生成订单
记忆钩子:先给结论,再拆流程,再讲数字例子和失败边界;回答「秒杀流量控制」时要围绕题目问法收束,不要把相邻概念堆成一段没有重点的名词清单。
- 误区:限流就是简单拒绝请求。 合格答案要区分放行、排队、快速失败、降级、退避和熔断,而不是只说“挡住流量”。
- 误区:全局平均流量低就没有风险。 热点 key、单实例、单下游资源仍可能先被打满,所以要看局部水位。
- 误区:限流阈值可以拍脑袋配置。 阈值要来自压测容量、依赖水位、错误率、P99 延迟和业务优先级。
- 追问:限流应该放在哪里? 入口网关、服务内部、客户端和下游资源侧可以分层配置,分别保护不同边界。
- 追问:触发限流后怎么处理? 核心写请求可排队,非核心读请求可降级,用户侧要给明确失败或稍后重试。
- 追问:如何验证流控策略有效? 看 QPS、并发、延迟、拒绝率、队列长度、下游错误率和恢复时间。
九、加强记忆
秒杀流量控制可以记成“层层漏斗”:前端和 CDN 承接展示流量,网关挡住无效流量,缓存承接读流量,Redis 原子控制库存,MQ 把成功请求削峰,数据库只处理少量确定有效的订单。
面试时按“静态化 → 入口限流 → 热点缓存 → 原子扣库存 → MQ 削峰 → 幂等防重 → 降级应急”这个顺序回答,既能体现高并发思路,也能体现工程落地能力。