一、秒杀业务特点
- 瞬时高并发:短时间大量请求涌入,流量尖峰
- 热点商品:大量请求争抢同一份库存
- 读多写少:查询多,下单扣减库存写操作少
- 超卖风险:并发扣减库存,容易出现超卖
- 不能重下单:用户只能抢购 1 次,防重复下单
核心目标:防超卖、防重复下单、削峰、保护数据库
二、整体架构分层(由外到内,逐层拦截)
1. 前端层(第一层拦截)
- 按钮置灰:抢购结束前禁用按钮,防止重复点击
- 静态页面 CDN:商品图片、页面静态资源放 CDN,不请求后端
- 前端限流:控制短时间内用户请求次数(只能挡普通用户,防不住爬虫)
- 验证码 / 滑动验证:打散请求,避免同一时刻大量请求直接打到后端
2. 网关层(Nginx/SpringCloud Gateway)
- IP 限流、接口限流,直接拦截恶意爬虫、高频请求
- 黑名单:拉黑恶意 IP
- 负载均衡,分发流量到后端实例
3. 应用层(Sentinel)
- 接口限流、热点参数限流(按商品 id 限流)
- 熔断降级:非核心接口(推荐、评论)直接降级,保障下单核心链路
上面三层目的:把无效请求尽可能在前面拦截,不要打到库存服务
4. 缓存层 Redis(核心)
- 预加载库存:秒杀开始前,把商品库存预热到 Redis
- 库存预扣减:Redis 执行扣减,Lua 脚本保证原子性,防止超卖
- 用户抢购标记:Redis 记录用户 + 商品,判断该用户是否已经下单,防止重复抢购
Redis 只做预扣,不直接创建订单
5. MQ 消息队列(削峰)
Redis 预扣库存成功后,发送下单消息到 MQ,异步创建订单
✅作用:削峰,把瞬时并发请求转为平稳消费;数据库按消费速度慢慢处理订单
❌风险:MQ 消息丢失,需要消息确认 + 幂等
6. 数据库层(最终落地)
- 数据库库存作为最终一致性兜底
- 分库分表,订单表做水平拆分
- 唯一索引:用户 id + 商品 id,兜底防止同一用户多次下单
三、两大核心难题:超卖 & 重复下单
1. 如何防止超卖
方案:Redis + Lua 脚本(主流)
Lua 脚本一次性完成:判断库存 > 0,库存减 1,返回成功;否则返回失败。单条脚本原子执行,避免并发问题。
兜底方案:数据库乐观锁update stock set count=count-1 where id=xxx and count>0
不要用悲观锁,高并发下锁等待,性能极差。
2. 防止用户重复抢购
Redis 记录 key:seckill:user:商品id,抢购成功写入;请求进来先判断 key 是否存在; 数据库增加唯一索引(user_id + product_id)兜底。
四、完整秒杀请求流程
- 用户发起秒杀请求 → CDN / 前端校验
- Gateway 网关限流,非法请求直接拒绝
- 后端 Sentinel 热点限流
- Redis Lua 脚本:校验库存 + 判断用户是否已抢购 + 预扣库存
- 失败:直接返回抢购失败
- 成功:发送消息到 MQ
- 返回前端:排队中
- MQ 消费者异步消费,创建订单,扣减数据库库存
- 订单创建成功,通知用户抢购成功
五、库存不一致问题(Redis 库存和 DB 库存不一致)
场景:Redis 预扣成功,但是 MQ 消费失败,数据库订单创建失败 → Redis 库存少扣,库存不一致。
解决方案:
- 定时对账任务:后台定时对比 Redis 库存和 DB 库存,发现差异补偿回滚
- 消息消费失败死信队列,人工处理异常消息
- 设置超时回滚:用户下单后 N 分钟未支付,自动释放 Redis 和 DB 库存
六、超时未支付,库存回滚
- 用户抢到后,一定时间(比如 15 分钟)未支付,自动释放库存
- 实现:Redis 设置过期;或者延时队列 / RabbitMQ 死信队列
- 回滚逻辑:恢复 Redis 库存,更新订单状态,库存加回
七、高频深挖面试题
Q1:为什么不能直接操作数据库扣库存?
A:瞬时几万并发请求打到 DB,数据库连接耗尽,直接宕机。数据库扛不住尖峰流量,所以前置 Redis+MQ 削峰。
Q2:Redis 扣减库存为什么要用 Lua?
A:多命令(判断库存、扣库存)不是原子,并发场景会超卖;Lua 脚本在 Redis 服务端一次性执行,保证原子性。
Q3:Redis 宕机怎么办?
A:Redis 集群主从 + 持久化;Redis 只是缓存,数据库库存作为最终数据源。Redis 挂掉,秒杀暂停,恢复后对账。
Q4:为什么要用 MQ 异步下单?
A:削峰。同步创建订单会大量占用数据库连接;MQ 把瞬时流量变成匀速消费。
Q5:秒杀可以用分布式锁吗?
A:不推荐。分布式锁性能差,并发场景下大量线程等待,吞吐量低,一般优先 Lua。
Q6:热点商品怎么优化?
A:本地缓存 + Redis 多级缓存;Sentinel 热点参数限流;隔离热点商品独立服务,避免影响其他业务。
八、背诵小结
秒杀核心思路:层层拦截、前端 + 网关限流,Redis 预扣库存防超卖,MQ 异步下单削峰,数据库兜底。
两大难点:Lua 脚本防超卖;Redis + 唯一索引防用户重复下单;库存不一致靠定时对账 + 死信处理;未支付订单用死信 / 延时队列回收库存。