
一、架构设计核心思想
秒杀系统的核心矛盾是瞬时高并发请求与系统有限处理能力的不匹配,因此设计围绕三大核心思路展开:
- 拦截:在请求到达核心业务逻辑前,通过多层过滤筛选掉无效、恶意或超量请求,减少下游压力;
- 缓冲:通过缓存、队列等组件 “削峰填谷”,将瞬时峰值请求转化为平稳的后台处理流量;
- 异步:拆分核心链路与非核心链路,核心链路(如扣库存)同步快速响应,非核心链路(如创建订单、通知)异步延迟处理,提升前端响应速度。
二、整体架构图
plaintext
用户层 → CDN → 网关层 → 业务层 → 缓存层 → 消息队列 → 数据层
↓ ↓ ↓ ↓
限流 防作弊 库存预加载 异步处理
↓ ↓ ↓ ↓
降级策略 频率控制 原子扣库存 最终一致性
详细架构拆解(从请求入口到数据落地)
如下图:

三、各层级设计细节
1. 网关层:请求入口的 “第一重拦截”
核心目标:负载均衡 + 限流 + 初步过滤,避免无效请求进入业务层。
- Nginx 反向代理:
- 部署多台 Nginx 组成集群(配合 Keepalived 实现高可用),将百万 QPS 请求均匀分发到后端业务服务器;
- 配置
proxy_connect_timeout等参数缩短无效连接超时时间,减少资源占用; - 静态资源(如活动页面、按钮图标)直接通过 Nginx 返回,无需穿透到业务层。
- 网关限流(Sentinel/Nginx 限流模块):
- 基于 “令牌桶算法” 或 “漏桶算法”,设置网关总 QPS 阈值(如 120 万,预留 20% 缓冲),超过阈值的请求直接返回
503 Service Unavailable; - 按 “用户 IP + 游戏 ID” 维度设置细粒度限流(如单 IP 每秒最多 10 次请求),防止单机恶意刷量。
- 基于 “令牌桶算法” 或 “漏桶算法”,设置网关总 QPS 阈值(如 120 万,预留 20% 缓冲),超过阈值的请求直接返回
2. 业务层:核心逻辑的 “第二重过滤”
核心目标:防作弊 + 业务校验 + 降级控制,确保只有合法请求进入缓存层。
- 防作弊策略:
- 频率限制:通过 Sentinel 对 “用户 ID” 设置限流(如单用户活动期间最多 3 次抢购请求),避免单用户反复提交;
- 合法性校验:校验用户是否已登录、是否满足游戏内参与条件(如等级≥10 级)、请求参数是否篡改(如签名验证),非法请求直接拦截。
- 降级机制:
- 触发条件:当网关限流触发率≥80%、Redis 响应延迟≥500ms 或业务服务器 CPU 利用率≥90% 时,自动触发降级;
- 降级方案:
- 排队模式:返回 “当前拥挤,请排队等待”,通过前端轮询 + Redis 队列记录用户排队顺序,待系统压力下降后依次处理;
- 抽签模式:用户提交请求后仅记录 “参与资格”,活动结束后通过随机算法抽取中奖用户,再异步处理订单,彻底规避瞬时峰值。
3. 缓存层:库存操作的 “核心战场”
核心目标:原子扣库存 + 快速响应,避免百万请求直接打穿数据库(数据库单库 QPS 通常仅 1 万 - 5 万,无法承载峰值)。
- 库存预加载:
- 活动开始前 1 小时,通过定时任务将秒杀商品库存(如 10 万份游戏道具)从数据库同步到 Redis,存储结构为
key=seckill:game:1001(游戏ID):goods:2001(商品ID), value=100000; - 为防止 Redis 单点故障,采用 “主从 + 哨兵” 架构(1 主 3 从),主库负责写操作(扣库存),从库负责读操作(查询库存),主库故障时哨兵自动切换从库为主库。
- 活动开始前 1 小时,通过定时任务将秒杀商品库存(如 10 万份游戏道具)从数据库同步到 Redis,存储结构为
- 原子扣库存:
- 采用 Redis 的
Lua脚本实现原子操作(避免 “超卖” 问题),脚本逻辑如下:lua
-- 1. 检查库存是否充足 local stock = redis.call('GET', KEYS[1]) if tonumber(stock) <= 0 then return 0 -- 库存不足,返回失败 end -- 2. 扣减库存(DECR原子操作) redis.call('DECR', KEYS[1]) -- 3. 记录用户抢购记录(防止重复抢购) redis.call('SADD', KEYS[2], ARGV[1]) -- KEYS[2]为用户集合key,ARGV[1]为用户ID return 1 -- 扣库存成功 - 关键:通过
SADD记录用户抢购记录,后续请求可通过SISMEMBER快速判断用户是否已抢购,避免重复扣库存。
- 采用 Redis 的
4. 异步层:非核心逻辑的 “削峰填谷”
核心目标:拆分核心链路与非核心链路,让前端快速响应,后台平稳处理。
- 消息队列(MQ)选型:
- 选用 RabbitMQ 或 RocketMQ(支持高吞吐 + 事务消息),确保消息不丢失、不重复消费;
- 配置 “死信队列”,处理失败的消息(如创建订单失败)可重试 3 次,仍失败则进入死信队列人工排查。
- 异步处理流程:
- 当 Redis 扣库存成功后,业务层立即向 MQ 发送 “抢购成功” 消息(携带用户 ID、商品 ID、抢购时间);
- MQ 消费者集群异步处理 3 类任务:
- 订单创建:根据消息生成订单数据,存储到订单库;
- 库存同步:将数据库中对应商品的库存同步扣减(与 Redis 库存最终保持一致);
- 通知发送:调用短信 / 推送接口,告知用户抢购结果。
- 最终一致性保障:通过 “Redis 库存 + 数据库库存” 双校验,活动结束后执行库存对账任务,若存在差异则以 Redis 为准修正数据库。
5. 数据层:最终数据的 “持久化保障”
核心目标:高可用 + 读写分离,支撑异步层的平稳写入。
- 数据库架构:
- 采用 “主从分离” 架构,主库负责异步写入(创建订单、扣库存),从库负责查询(如用户查询订单);
- 按 “商品 ID” 分库分表(如 10 个分库,商品 ID%10 路由到对应分库),避免单库写入压力过大;
- 配置数据库连接池(如 HikariCP),设置合理的连接数(如单库最大 500 连接),避免连接耗尽。
- 数据一致性:
- 活动结束后,执行 “Redis 库存 vs 数据库库存” 对账任务,若 Redis 库存<数据库库存(可能因 MQ 消息延迟),则修正数据库库存为 Redis 值;
- 订单数据通过 “定时任务 + 日志回放” 确保不丢失,若 MQ 消费者故障,可通过日志重新消费消息。
四、关键问题解决方案
| 核心问题 | 解决方案 |
|---|---|
| 超卖问题 | 1. Redis 扣库存使用 Lua 脚本保证原子性;2. 数据库库存作为最终校验,活动结束后对账修正 |
| 重复抢购 | 1. Redis 用SADD记录用户抢购记录,重复请求直接拦截;2. 订单表设置 “用户 ID + 商品 ID” 唯一索引 |
| 系统高可用 | 1. Nginx+Keepalived 高可用;2. Redis 主从 + 哨兵;3. 业务层 / 数据库层集群部署;4. 关键组件故障自动切换 |
| 瞬时峰值压力 | 1. 网关 + 业务层多层限流;2. Redis 承载所有库存操作;3. MQ 异步削峰,将瞬时请求转化为平稳流量 |
五、架构优势总结
- 高吞吐:通过 Nginx 负载均衡、Redis 高并发扣库存,支撑百万 QPS 峰值;
- 高可用:关键组件(Nginx、Redis、数据库)均为集群架构,无单点故障;
- 防超卖 / 防作弊:Lua 原子操作 + 用户频率限制 + 数据一致性校验,避免业务风险;
- 用户体验:核心链路(扣库存 + 返回结果)耗时<100ms,异步处理非核心逻辑,前端快速响应。

645

被折叠的 条评论
为什么被折叠?



