【项目深入】二、秒杀系统

1. 设计目标

  • 高并发处理能力:支撑十万级 QPS 的瞬时流量冲击
  • 严格防超卖:确保商品库存不被超额扣减
  • 最终一致性保障:Redis 与数据库之间允许短暂不一致,但需可靠收敛
  • 低延迟响应:核心路径(如库存扣减)控制在毫秒级
  • 系统可降级:在 Redis 故障或流量异常时,具备熔断与兜底机制

2. 架构分层策略

2.1 前端防护层

  • 静态资源托管:秒杀页面采用纯静态 HTML + CDN 分发,彻底剥离后端渲染压力
  • 请求合法性校验
    • 用户需提前获取一次性秒杀 Token(由后端签发,含用户 ID、活动 ID、时间戳、签名)
    • Token 有效期极短(如 1~3 秒),防止重放攻击
    • 结合设备指纹(如 canvas、WebGL、UserAgent 特征哈希)识别异常客户端
  • 交互限制:按钮点击后立即置灰,前端禁止重复提交

2.2 网关限流层

  • 全局限流:基于令牌桶算法(如 Guava RateLimiter 或 Sentinel)控制入口总流量
  • 用户级限流:对同一用户 ID 实施"1 秒 1 次"请求频率限制(可通过 Redis INCR + EXPIRE 实现)
  • 异常流量拦截:集成风控系统,自动封禁高频 IP 或设备 ID

2.3 防刷与安全机制

  • 图形验证码:在秒杀前触发滑块/点选验证码(仅对可疑流量或高并发时段启用,避免影响正常用户)
  • 限购策略:每人最多购买 N 件(通过 Redis 记录用户已购数量)
  • 黑名单机制:对历史作弊用户/设备/IP 实施永久或临时封禁

3. 核心库存扣减方案(Redis 主路径)

3.1 预热与原子操作

  • 秒杀开始前,将商品库存预加载至 Redis(如 stock:product_123 = 100

  • 扣减操作通过 Lua 脚本 保证原子性,脚本逻辑如下:

    lua 复制代码
    local stock = tonumber(redis.call('GET', KEYS[1]))
    if stock > 0 then
        redis.call('DECR', KEYS[1])
        return 1  -- 扣减成功
    end
    return 0  -- 库存不足
  • 同时记录用户购买状态(如 user_bought:user_456:product_123 = 1),防止重复下单

3.2 异步订单生成

  • 扣减成功后,向 消息队列(如 Kafka / RocketMQ) 发送下单消息
  • 下游订单服务消费消息,写入数据库订单表,并更新 DB 库存(最终一致性
  • 订单表设计包含唯一索引(如 user_id + product_id),确保幂等性

3.3 多级缓存

  • L1 缓存(本地缓存):缓存活动配置(如开始时间、限购数),减少 Redis 访问
  • L2 缓存(Redis):承载核心库存数据与用户购买状态

4. 数据库分片方案(DB 主路径,作为备选或特定场景)

4.1 分片策略

  • 将 100 件库存逻辑拆分为 10 个 DB 分片(如按 user_id % 10 路由)
  • 每个分片独立维护子库存(如 shard_0.stock = 10
  • 并发请求被分散到不同分片,消除单行锁竞争,提升整体吞吐

4.2 优势与局限

优势 局限
无需 Redis,天然强一致 无法实时获取全局剩余库存
单分片内 ACID 保证不超卖 分片扩容困难,需预估容量
无缓存-DB 对账复杂度 热点用户仍可能集中在某一分片

适用场景:对一致性要求极高、且可接受库存统计延迟的业务(如金融类秒杀)


5. Redis 与 DB 一致性保障

  • 采用 "延迟双删" 策略:
    1. 删除 Redis 缓存
    2. 更新数据库
    3. 延迟 N 毫秒(如 500ms)后再次删除缓存(应对主从同步延迟)
  • 增强方案 :通过 MQ 异步触发缓存删除
    • 若删除失败,进入指数退避重试(1s, 2s, 4s...)
    • 重试 3~5 次仍失败,转入死信队列,告警并人工介入
  • 定期对账任务:每日离线比对 Redis 库存、订单表、DB 库存,自动修复差异

6. 容灾与降级机制

6.1 Redis 故障应对

  • 熔断策略:当 Redis 连接失败率超过阈值,立即熔断 Redis 路径
  • 降级方案
    • 方案 A:直接返回"秒杀繁忙,请稍后再试",保护 DB
    • 方案 B:切换至 DB 分片模式(若已部署),但限流至极低 QPS(如 100/s)
  • 禁止直连 DB 扣库存:避免 DB 被高并发写请求压垮

6.2 Redis 快速恢复

  • 高可用部署:Redis Cluster + 哨兵(Sentinel)实现自动主从切换
  • 容器化运维:K8s 自动重启故障 Pod,配合健康检查
  • 缓存预热脚本 :服务启动时,从 DB 批量加载热点商品库存至 Redis,避免缓存击穿

7. 总结:双模架构选型建议

场景 推荐方案
极致性能 + 可接受最终一致 Redis 主路径 + MQ 异步落库
强一致性 + 中低并发 DB 分片方案
混合部署 Redis 为主,DB 分片为灾备降级路径

通过 Token 防重 + 网关限流 + Lua 原子扣减 + MQ 异步解耦 + 多级容灾,构建高可用、可扩展、防超卖的秒杀系统。

相关推荐
白鲸开源1 小时前
干货!SeaTunnel(2.3.12)高阶用法(一):核心概念之数据流
java·大数据·github
花开·莫之弃1 小时前
Mac安装多版本jdk(jenv)
java·开发语言·macos
计算机安禾1 小时前
【c++面向对象编程】第32篇:移动语义与右值引用:现代C++性能优化核心
java·c++·性能优化
JAVA面经实录9172 小时前
JVM高频面试总结(背诵完整版)
java·开发语言·jvm
ChoSeitaku2 小时前
11.异常_throws_try...catch_BigInteger_BigDecimal_Date_Calendar_LocalDate_Integer
java
胡志辉的博客2 小时前
完全开源、本地 SQLite 管理一切:我写了一个桌面邮件客户端 OneMail
java·sqlite·开源
沪漂阿龙2 小时前
Java JVM 面试题详解:JVM运行原理、内存模型、堆栈方法区、GC垃圾回收、JIT编译、类加载机制与线上调优全攻略
java·开发语言·jvm
小碗羊肉2 小时前
Maven高级
java·开发语言·maven
星秀日2 小时前
Spring Boot + Sa-Token 实时聊天系统:用户注册流程源码深度剖析
java·人工智能·spring·状态模式