☰
Java高级后端 · 全套面试通关手册(秒杀系统设计)
2026/10/17 7:08:43 网站建设 项目流程

一、秒杀业务特点

  1. 瞬时高并发:短时间大量请求涌入,流量尖峰
  2. 热点商品:大量请求争抢同一份库存
  3. 读多写少:查询多,下单扣减库存写操作少
  4. 超卖风险:并发扣减库存,容易出现超卖
  5. 不能重下单:用户只能抢购 1 次,防重复下单

核心目标:防超卖、防重复下单、削峰、保护数据库

二、整体架构分层(由外到内,逐层拦截)

1. 前端层(第一层拦截)

  • 按钮置灰:抢购结束前禁用按钮,防止重复点击
  • 静态页面 CDN:商品图片、页面静态资源放 CDN,不请求后端
  • 前端限流:控制短时间内用户请求次数(只能挡普通用户,防不住爬虫)
  • 验证码 / 滑动验证:打散请求,避免同一时刻大量请求直接打到后端

2. 网关层(Nginx/SpringCloud Gateway)

  • IP 限流、接口限流,直接拦截恶意爬虫、高频请求
  • 黑名单:拉黑恶意 IP
  • 负载均衡,分发流量到后端实例

3. 应用层(Sentinel)

  • 接口限流、热点参数限流(按商品 id 限流)
  • 熔断降级:非核心接口(推荐、评论)直接降级,保障下单核心链路

上面三层目的:把无效请求尽可能在前面拦截,不要打到库存服务

4. 缓存层 Redis(核心)

  1. 预加载库存:秒杀开始前,把商品库存预热到 Redis
  2. 库存预扣减:Redis 执行扣减,Lua 脚本保证原子性,防止超卖
  3. 用户抢购标记: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)兜底。

四、完整秒杀请求流程

  1. 用户发起秒杀请求 → CDN / 前端校验
  2. Gateway 网关限流,非法请求直接拒绝
  3. 后端 Sentinel 热点限流
  4. Redis Lua 脚本:校验库存 + 判断用户是否已抢购 + 预扣库存
    • 失败:直接返回抢购失败
    • 成功:发送消息到 MQ
  5. 返回前端:排队中
  6. MQ 消费者异步消费,创建订单,扣减数据库库存
  7. 订单创建成功,通知用户抢购成功

五、库存不一致问题(Redis 库存和 DB 库存不一致)

场景:Redis 预扣成功,但是 MQ 消费失败,数据库订单创建失败 → Redis 库存少扣,库存不一致。

解决方案:

  1. 定时对账任务:后台定时对比 Redis 库存和 DB 库存,发现差异补偿回滚
  2. 消息消费失败死信队列,人工处理异常消息
  3. 设置超时回滚:用户下单后 N 分钟未支付,自动释放 Redis 和 DB 库存

六、超时未支付,库存回滚

  1. 用户抢到后,一定时间(比如 15 分钟)未支付,自动释放库存
  2. 实现:Redis 设置过期;或者延时队列 / RabbitMQ 死信队列
  3. 回滚逻辑:恢复 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 + 唯一索引防用户重复下单;库存不一致靠定时对账 + 死信处理;未支付订单用死信 / 延时队列回收库存。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询