从零讲清如何设计一个高并发“秒杀”系统?
大约 4 分钟
从零讲清如何设计一个高并发“秒杀”系统?
一句话介绍什么是秒杀?
秒杀(flash sale)= 瞬间有大量用户同时请求一个极少的库存资源(比如抢 10 台手机,100 万人点进来)
它是对系统并发能力、限流策略、数据一致性、业务稳定性的全方位考验。
秒杀系统的核心难点(我们必须解决)
- 瞬时流量暴涨:百万请求在 1 秒内涌入
- 防止超卖:库存不能卖超
- 用户去重:一个用户只能买一次
- 系统抗压:要保护核心系统不被打挂
- 兜底方案:即使秒杀失败,也能优雅提示
架构设计整体思路(面试必讲)
我们可以从 前端 → 接入层 → 服务层 → 缓存层 → 消息队列 → 数据库 这条链路来设计:
前端层设计
| 技术点 | 说明 |
|---|---|
| CDN 缓存 | 将静态资源(秒杀页面 HTML、JS)放入 CDN,减少服务压力 |
| 提前下发时间戳 | 客户端 JS 中提前获取服务器时间,控制按钮开放时间 |
| 点击节流 | 控制用户频繁点击、短时间多次请求 |
网关接入层设计
| 技术点 | 说明 |
|---|---|
| IP 黑名单 | 一些明显刷子、异常行为的 IP 拦截 |
| 限流 | 网关对 URI 做 QPS 限流(如 Nginx + lua、Spring Gateway) |
| 验证码 | 加一道图形验证,降低机器人攻击风险 |
后端服务层设计
1. 请求过滤(先过滤掉无资格人群)
- 用户是否登录?
- 是否已经参与过本场活动?
- 活动是否已经开始 / 结束?
2. 接口限流(控制单位时间并发量)
可以用:
- 令牌桶:固定速率发放处理权限
- 漏斗限流:固定速率处理,突发排队
核心业务层:如何防止超卖?
重点就是 —— 减库存的原子性 + 幂等性
做法一:Redis 扣库存(推荐)
- 活动开始前将库存数量预热到 Redis:
SET goods_stock_1001 10- 秒杀接口内 Redis 执行:
-- Lua 脚本原子操作
if redis.call("get", KEYS[1]) > 0 then
redis.call("decr", KEYS[1])
return 1
else
return 0
end优势:
- Redis 是内存级访问,极快
- Lua 保证原子性,避免并发问题
引入消息队列(MQ)做削峰
为什么还需要 MQ?
因为 Redis 扣完库存后,后续下单逻辑仍然很重,比如创建订单、写库、通知支付…
所以用 MQ 把下单流程拆出来,先响应“秒杀成功排队中”,异步下单。
秒杀逻辑分两步
- 用户请求 → Redis 扣库存 → MQ 入队
- 消费者异步监听 MQ → 创建订单 → 写数据库
幂等校验 & 数据库一致性
| 问题 | 解决方法 |
|---|---|
| 一个用户多次下单 | Redis 或 DB 做幂等校验(Set) |
| 超卖 | Redis 扣库存为主,DB做二次校验 |
| 支付未完成订单 | 设置订单 TTL,超时回滚库存 |
示例:核心逻辑伪代码(Redis + MQ)
public String doSeckill(String userId, String goodsId) {
// 幂等校验
if (redis.sismember("seckill_users_" + goodsId, userId)) {
return "我们已参与过该秒杀";
}
// 扣库存
Long stock = redis.decr("seckill_stock_" + goodsId);
if (stock < 0) {
return "商品已抢光";
}
// 加入已抢用户
redis.sadd("seckill_users_" + goodsId, userId);
// 发送 MQ 下单
mq.send("seckill_order", new Order(userId, goodsId));
return "排队中,请稍后...";
}秒杀系统完整架构图(建议配图)
用户 → 网关限流 → 业务接口层 → Redis 扣库存 → MQ 下单 → DB 落库常见优化点
| 优化点 | 描述 |
|---|---|
| 本地缓存活动配置 | 降低每次访问数据库 |
| 分段库存预热 | 拆分库存到多个 Redis Key,降低热点 |
| 动态验证码 | 开始前 1 分钟才发验证码,提高防刷效果 |
| 队列排队可视化 | 返回前端“排队中界面”,优化用户体验 |
总结归纳
秒杀系统的本质是控制并发洪峰流量的过程,通过前端减压、接入层限流、Redis 扣库存、MQ 削峰、幂等校验等手段层层保护核心系统,并且需要做到高性能、高可用、高一致性。
工程里的关键补充点
库存一致性与回滚
- Redis 扣库存成功但下单失败时要回滚
- 订单超时未支付要释放库存
幂等与防刷
- 幂等 key 需要和用户、活动绑定
- 防刷不仅靠验证码,还要有行为风控
限流与降级
- 网关限流要与业务容量匹配
- 超过阈值时直接降级提示,保护核心服务
监控与容量评估
- 监控排队长度、失败率、超卖次数
- 活动前做容量压测与预案演练
