Java面试要点:活动预热、限流、排队、库存预扣与防超卖
前言
秒杀题是高并发面试中的经典场景。它考察的不只是 Redis 扣库存,而是从入口限流、活动预热、库存模型、异步下单、幂等、防刷、降级到补偿的完整链路。
秒杀链路
flowchart TD
A[用户请求] --> B[网关限流]
B --> C[活动资格校验]
C --> D[Redis 原子扣库存]
D --> E{扣减成功}
E -->|否| F[售罄返回]
E -->|是| G[写入 MQ]
G --> H[异步创建订单]
H --> I[支付超时关闭/库存回滚]
活动预热
秒杀开始前应预热:
- 商品信息。
- 活动配置。
- 库存数量。
- 用户资格。
- 页面静态资源。
预热目的:
- 避免开始瞬间大量回源数据库。
- 降低首波请求延迟。
- 提前发现配置错误。
入口限流
限流不是可选项。
常见层次:
| 层次 | 手段 |
|---|---|
| CDN/前端 | 静态化、按钮置灰、随机倒计时 |
| 网关 | IP 限流、用户限流、令牌桶 |
| 服务 | 接口级限流、线程池隔离 |
| 数据层 | Redis 原子脚本、数据库兜底 |
面试表达:
秒杀系统要先挡流量,再处理业务。不能让所有请求都打到库存和订单服务。
库存扣减方案
方案一:数据库扣库存
1 | update sku |
优点是简单准确,缺点是高并发下数据库压力大。
方案二:Redis 原子扣库存
用 Lua 保证库存判断和扣减原子执行。
优点是性能高,缺点是要处理 Redis 与数据库一致性。
方案三:分段库存
把库存拆成多个段或桶,降低单点热点。
适合极高并发活动,但实现复杂。
异步下单
Redis 扣减成功后,不建议直接同步落库创建订单,可以写入 MQ 由消费者异步创建。
好处:
- 下单接口快速返回。
- 订单服务按能力消费。
- 削峰填谷。
风险:
- 消息积压。
- 消费失败。
- 用户看到“抢购成功”但订单延迟生成。
因此要有:
- 抢购结果查询。
- 消费幂等。
- 失败补偿。
防刷和防重复
常见手段:
- 活动 Token。
- 用户限购。
- IP 限流。
- 设备指纹。
- 验证码。
- 黑名单。
- 风控规则。
用户限购建议用 Redis 或数据库唯一约束兜底:
1 | user_id + activity_id 唯一 |
库存回滚
回滚场景:
- MQ 消费失败。
- 订单创建失败。
- 订单超时未支付。
- 用户取消。
回滚要幂等,不能重复加库存。一般通过订单状态和库存流水控制。
高频面试题
秒杀为什么要异步下单?
因为同步下单会把订单库打爆。异步下单可以削峰,让订单服务按自身能力消费。
Redis 扣库存会不会超卖?
用 Lua 原子判断和扣减可以避免 Redis 层超卖,但仍要处理消息失败、落库失败和库存回滚。
怎么防止一个用户重复抢?
用用户活动维度的唯一键、Redis 去重、数据库唯一约束和消费幂等共同保证。
秒杀系统为什么要降级?
因为大促流量不可完全预测。降级可以保护核心链路,避免整体系统被拖垮。
总结
秒杀抢购的主线是:入口挡流量、活动先预热、Redis 原子扣库存、MQ 异步下单、订单幂等落库、失败补偿回滚。面试回答要体现“抗压优先,最终一致兜底”。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Junly博客!
评论


