分布式高并发架构 / 系统设计文档 001 — FLASH SALE

百万 QPS 秒杀系统
架构设计:
多级缓存 与 流量整形。

秒杀系统是分布式高并发架构的试金石。电商大促、限时抢购场景下,短时间涌入海量请求,瞬时 QPS 可达数十万甚至百万级别。本文由外向内逐层拆解完整架构,覆盖 流量拦截、限流、多级缓存、库存扣减、异步削峰 全链路方案,附带工程落地实践。

峰值 QPS
1,000,000
架构分层
6LAYERS
DB 实际承载
~100QPS
超卖风险
0ZERO
OVERVIEW
00/06

整体架构 分层思想

流量处理遵循前置拦截、层层过滤原则,尽可能在离用户更近的上层丢弃无效请求,避免请求穿透到后端业务与数据库。下方为六层架构的流量削减路径——百万 QPS 在抵达数据库前已被收敛至百级。

L1
CDN + WAF
静态资源托管 · 恶意流量清洗 · 爬虫拦截
1,000,000
QPS IN
▼
— 削减 50% —
L2
API Gateway
令牌桶限流 · 鉴权 · 流量整形
500,000
QPS
▼
— 削减 80% —
L3
应用服务层
业务校验 · 请求预处理 · 本地缓存读取
100,000
QPS
▼
— 削减 90% —
L4
多级缓存(L1 + L2 Redis)
库存查询 · 防重标记 · Lua 原子扣减
10,000
QPS
▼
— 削减 90% —
L5
消息队列 MQ
异步削峰 · RocketMQ / Kafka · 死信补偿
1,000
QPS
▼
— 削减 90% —
L6
MySQL 数据库
订单落地 · 库存持久化 · 分库分表
100
QPS OUT
CORE PRINCIPLE 原始流量 1,000,000 QPS 经六层削减后,仅 ~100 QPS 抵达数据库——削减率 99.99%。整套架构的核心目标即在此漏斗之上保证业务正确性。
LAYER 01
01/06

CDN 与 WAF 前置流量拦截

绝大多数秒杀活动页面(商品图片、活动静态页面、秒杀倒计时页面)属于静态资源。在这一层把羊毛党、爬虫、脚本请求直接挡掉,不进入业务网关。

STRATEGY
静态资源 CDN 托管

将秒杀活动首页、商品静态信息部署至 CDN,用户直接访问边缘节点,不会到达源站,大幅降低源站压力。动态倒计时可以前端本地时间兜底,配合接口轻量同步。

KEY POINT 大量羊毛党、脚本请求在此层直接拦截,不进入业务网关。
02 / WAF
WAF 防护恶意流量

通过 WAF 识别爬虫、高频刷接口、恶意脚本攻击:

  • 限制单 IP 访问频率;
  • 拦截没有合法 Cookie / Token 的非法请求;
  • 拦截特征明显的爬虫 UA。
TRAFFIC SPLIT
流量分流示意
cdn + waf — traffic split
01
02
03
04
05
06
07
08
// 静态资源走 CDN 边缘节点 request → CDN_EDGE → "静态页面/图片/JS/CSS" ↓ (命中率 ~95%) // 动态接口 + 可疑流量回源 request → WAF → { if ip_rate > 100/s → REJECT if no_token → REJECT if crawler_ua → REJECT else → forward_to_gateway }
LAYER 02
02/06

API 网关 流量整形 & 限流

请求通过外层防护后到达网关(Spring Cloud Gateway / Nginx),这一层核心做流量整形。秒杀不能简单粗暴直接拒绝流量,采用令牌桶算法平滑流量。

CORE ALGORITHM
令牌桶限流

提前根据服务器承载能力设置最大 QPS;匀速发放令牌,只有拿到令牌的请求才能进入下游服务;超出容量的请求直接返回"活动火爆,请重试"。

REQUESTS IN
500K REQ/S
TOKEN BUCKET
匀速发放 · 100K/s
PASSED / REJECTED
100K 通过 · 400K 拒绝
A / USER LEVEL
用户粒度限流
同一个账号短时间只能发起一次秒杀请求,防止单人重复抢购。基于 Redis SETNX + 短 TTL 实现。
B / GRAY RELEASE
灰度流量
大促前逐步放开流量阈值,验证系统承载能力。从 10% → 30% → 60% → 100% 阶梯放量。
C / FILTER
请求过滤
校验登录态、活动是否开始、商品是否下架,无效请求直接返回,不进入下游。
BEST PRACTICE 限流尽量放在网关实现,越早拦截,越节省后端服务器资源。
LAYER 03
03/06

多级缓存设计 架构核心

秒杀场景下,商品基础信息、是否开启秒杀、剩余库存标记属于高频读、低频写数据,依靠多级缓存挡下海量查询请求。L1 本地缓存挡住进程内查询,L2 Redis 承担核心状态。

L1 CACHE
应用本地缓存
Caffeine · 进程内存
用途
商品基础信息、活动配置
速度
纳秒级,不产生网络 IO
约束
缓存更新存在短暂不一致,适合允许秒级延迟的配置信息
容量
受限于应用进程内存
L2 CACHE
Redis 分布式缓存
Redis Cluster · 跨进程共享
用途
库存存储、分布式状态标记
速度
亚毫秒级,网络往返一次
约束
所有秒杀核心数据统一下沉,杜绝大量请求直接查询数据库
容量
分布式扩展,TB 级
CACHE DATA MANIFEST
Redis 缓存数据清单
  • 商品可秒杀库存数量(string / int)
  • 用户是否已经抢购成功(set,防重复下单)
  • 活动起止时间、商品状态(hash)
CONSISTENCY STRATEGY 秒杀属于高并发短时场景,接受短暂数据不一致。活动结束后统一同步数据至数据库,不采用强一致性方案避免性能损耗。
LAYER 04
04/06

库存原子扣减 Redis + Lua

超卖是秒杀最致命问题。如果分开执行"查询库存 → 判断库存 → 扣减库存",多线程并发下会出现竞态条件,最终库存扣多。解决方案:Lua 脚本原子执行。

WHY LUA
Lua 脚本原子性

Lua 脚本在 Redis 内部单线程执行,整条命令具备原子性,中间不会被其他命令打断。

RISK — 超卖 分离执行的三步在并发下会出现竞态:多个线程同时读到 stock=1,同时判断通过,同时扣减,最终库存变成负数。
RETURN CODES
返回值约定
  • return 1 · 抢购成功,具备下单资格
  • return 0 · 库存不足,抢购失败
  • return -1 · 重复抢购(用户已下单)

执行成功仅代表具备下单资格,不代表订单创建完成。

seckill.lua — atomic stock deduction
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
-- KEY[1] = 库存key,KEY[2] = 用户抢购记录key local stock = tonumber(redis.call('GET', KEYS[1])) if stock <= 0 then return 0 -- 库存不足,抢购失败 end -- 判断用户是否已经购买 local userBuy = redis.call('SISMEMBER', KEYS[2], ARGV[1]) if userBuy == 1 then return -1 -- 重复抢购 end -- 扣减库存、标记用户已下单 redis.call('DECR', KEYS[1]) redis.call('SADD', KEYS[2], ARGV[1]) return 1 -- 抢购成功
LAYER 05
05/06

MQ 异步削峰 隔离热点

Lua 脚本返回抢购成功后,不直接同步创建订单。同步创建订单会大量并发请求数据库,直接压垮 MySQL。改造为异步消费模式。

FLOW REFACTOR
异步流程改造
  1. 抢购成功 → 发送消息至消息队列(RocketMQ / Kafka);
  2. 服务消费者异步消费消息,执行创建订单、扣减数据库真实库存;
  3. 用户前端轮询或者 websocket 查询订单最终状态。
⚡
Lua 成功
return 1
→
📬
MQ Queue
RocketMQ · 持久化
→
⚙
Consumer
异步生成订单
01 / PERSIST
消息持久化
防止消息丢失导致成功抢到但没有生成订单。MQ 开启同步刷盘 + 主从同步。
02 / RATE LIMIT
消费者限流
控制消费速度,适配数据库处理上限。令牌桶或漏桶控制消费 TPS。
03 / DEAD LETTER
死信队列
订单创建失败的消息进入死信队列,人工排查补偿。避免毒消息阻塞正常消费。
LAYER 06
06/06

底层数据库 保护策略

数据库是整个架构最薄弱环节。原则:能不查库就不查库。

01
不参与实时库存判断
正常秒杀期间,数据库只做最终订单落地,不参与实时库存判断。库存判断全部在 Redis 完成。
02
定时兜底同步
Redis 库存数据定期和 MySQL 校对,防止缓存数据异常导致库存漂移。
03
分库分表
海量订单场景,订单表水平拆分,分散单表压力。按用户 ID 或订单时间分片。
04
简单 SQL 原则
禁止大事务、禁止复杂查询,消费者只执行简单 Insert、Update 语句。单事务毫秒级完成。
PITFALLS
!/!

常见线上坑点 落地避坑指南

五类高频踩坑场景,每一类都直接对应一次线上事故。规避的成本远低于复盘的成本。

01
只限流不做分层拦截
大量请求穿透直达 Redis、数据库,限流一定要前置到网关。CDN/WAF 层先挡掉静态资源与恶意流量,网关层再做令牌桶整形。
02
不控制重复下单
未做用户抢购标记,同一用户可以多次抢到商品。依靠 Lua 集合记录用户购买状态,第二次请求直接 return -1。
03
缓存雪崩、缓存击穿
秒杀商品热点 key,设置永不过期;不使用统一过期时间,避免大批量缓存同时失效。活动结束后手动清理。
04
同步创建订单
大量成功请求同步操作数据库,瞬间打满连接,必须异步 MQ 解耦。Lua 成功后立即返回"排队中",订单由消费者异步生成。
05
忽视流量预热
新 Redis 集群冷启动直接承接流量,建议活动前提前预热缓存数据。商品库存、活动配置、用户白名单等提前写入。
EXECUTION FLOW
07/07

架构整体 执行流程

一个秒杀请求从用户发起至最终订单生成的完整链路。整套架构通过流量层层削减,把百万 QPS 原始流量逐步收敛,最终仅有少量有效请求抵达数据库。

01
CDN / WAF
用户发起请求
CDN 清洗爬虫、静态资源分流
02
GATEWAY
网关限流
令牌桶整形、非法请求拦截
03
SERVICE
业务服务
本地缓存读取活动配置
04
REDIS + LUA
原子校验
库存检查、防重复下单
05
MQ
异步削峰
发送消息、返回"排队中"
06
CONSUMER
订单生成
异步创建订单、更新 DB 库存
07
CLIENT
结果回查
前端轮询获取最终订单结果
CONCLUSION 整套架构通过流量层层削减,把百万 QPS 原始流量逐步收敛,最终仅有少量有效请求抵达数据库,在保障业务正确性的前提下,解决高并发秒杀场景稳定性难题。