八股文解析
项目场景:如何设计秒杀系统?
项目场景:如何设计秒杀系统?
一句话结论
- 核心矛盾:瞬时高并发 vs 库存有限
- 解决思路:前端限流 + 队列削峰 + Redis 预减库存 + DB 异步落库 + 兜底防超卖
面试标准答法
整体架构
用户 → CDN/前端 → 网关/限流 → 秒杀服务 → Redis(预减库存+排队)→ MQ → 订单服务 → MySQL关键设计点
1. 前端与网关层
- 静态页面 CDN 缓存,按钮置灰倒计时
- 秒杀 URL 动态化,防提前刷单
- 网关层令牌桶限流(如 1000 QPS),多余直接返回「已抢完」
2. Redis 预减库存
- 活动前把库存加载到 Redis:
stock:sku_123 = 100 - 每次请求
DECR,返回值 < 0 则秒杀失败 - 用 Lua 脚本保证「判断 + 扣减」原子性
3. 消息队列削峰
- Redis 扣减成功后,发消息到 MQ(如 RocketMQ/Kafka)
- 订单服务消费 MQ,异步创建订单、扣 DB 库存
- 流量被 MQ 平摊,DB 压力可控
4. 数据库防超卖兜底
- 乐观锁:
UPDATE stock SET num = num - 1 WHERE sku_id = ? AND num > 0 - 或唯一约束防重复下单:
UNIQUE(user_id, activity_id)
5. 限流降级与隔离
- 热点商品单独 Redis key + 本地缓存
- 非核心功能(积分、通知)降级或延后
- 秒杀服务独立部署,不拖垮主站
常见追问
| 追问 | 要点 |
|---|---|
| Redis 和 DB 库存不一致怎么办? | Redis 只做预减,DB 为最终一致;未支付订单超时回滚 Redis |
| 如何防刷子? | 验证码、滑块、IP 限频、用户风控、隐藏秒杀地址 |
| 为什么还要 MQ? | 削峰填谷,把瞬时 10w QPS 摊成 DB 能扛的 2k TPS |
| 超卖怎么完全避免? | Redis Lua + DB 乐观锁双重校验 |
面试回答模板(30 秒版)
延伸准备
- 能画出秒杀时序图
- 能解释为什么不用 DB 直接抗(行锁竞争、连接打满)
- 能对比「悲观锁 vs 乐观锁 vs Redis 原子扣减」的取舍