前言

秒杀题是高并发面试中的经典场景。它考察的不只是 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
2
3
update sku
set stock = stock - 1
where sku_id = ? and stock > 0;

优点是简单准确,缺点是高并发下数据库压力大。

方案二:Redis 原子扣库存

用 Lua 保证库存判断和扣减原子执行。

优点是性能高,缺点是要处理 Redis 与数据库一致性。

方案三:分段库存

把库存拆成多个段或桶,降低单点热点。

适合极高并发活动,但实现复杂。

异步下单

Redis 扣减成功后,不建议直接同步落库创建订单,可以写入 MQ 由消费者异步创建。

好处:

  • 下单接口快速返回。
  • 订单服务按能力消费。
  • 削峰填谷。

风险:

  • 消息积压。
  • 消费失败。
  • 用户看到“抢购成功”但订单延迟生成。

因此要有:

  • 抢购结果查询。
  • 消费幂等。
  • 失败补偿。

防刷和防重复

常见手段:

  • 活动 Token。
  • 用户限购。
  • IP 限流。
  • 设备指纹。
  • 验证码。
  • 黑名单。
  • 风控规则。

用户限购建议用 Redis 或数据库唯一约束兜底:

1
user_id + activity_id 唯一

库存回滚

回滚场景:

  • MQ 消费失败。
  • 订单创建失败。
  • 订单超时未支付。
  • 用户取消。

回滚要幂等,不能重复加库存。一般通过订单状态和库存流水控制。

高频面试题

秒杀为什么要异步下单?

因为同步下单会把订单库打爆。异步下单可以削峰,让订单服务按自身能力消费。

Redis 扣库存会不会超卖?

用 Lua 原子判断和扣减可以避免 Redis 层超卖,但仍要处理消息失败、落库失败和库存回滚。

怎么防止一个用户重复抢?

用用户活动维度的唯一键、Redis 去重、数据库唯一约束和消费幂等共同保证。

秒杀系统为什么要降级?

因为大促流量不可完全预测。降级可以保护核心链路,避免整体系统被拖垮。

总结

秒杀抢购的主线是:入口挡流量、活动先预热、Redis 原子扣库存、MQ 异步下单、订单幂等落库、失败补偿回滚。面试回答要体现“抗压优先,最终一致兜底”。