Skip to main content

Redis 秒杀系统设计

决策

  • 预期并发量
  • 可接受的复杂度
  • 一致性要求(是否允许短暂不一致)
  • 团队维护能力

下面是一个完整的秒杀系统时序图(使用 Mermaid 语法),涵盖了从客户端发起请求到最终成功或失败的整个流程,并包含限流、防重、Redis 预减库存、异步消息队列、数据库最终扣减以及客户端轮询等关键步骤。

时序图关键点说明

  1. 限流防刷:Nginx 层做 IP 限流,业务层可进一步做用户级限流(例如使用令牌桶或漏桶)。
  2. 防重复秒杀:用户秒杀成功后,在 Redis 中记录 user:goodsId:userId,有效期设为一个活动周期,防止同一用户重复请求。
  3. Redis 预减库存:使用 Lua 脚本保证原子性(先 get 判断 >0 再 decr),避免超卖。这一步是系统的核心瓶颈保护点,支撑高并发。
  4. 异步消息队列:预减成功后立即返回“排队中”,秒杀请求异步化,不再同步等待数据库操作。
  5. 客户端轮询:用户端通过轮询结果接口获取最终状态,避免 WebSocket 长连接带来的资源消耗。
  6. 消费者数据库操作
    • 使用 update goods set stock = stock - 1 where goods_id = ? and stock > 0 作为最后防线,确保数据库不会出现负库存。
    • 订单表可建 (user_id, goods_id) 唯一索引,防止 MQ 重复消费导致重复下单。
  7. 补偿机制:若数据库更新失败(比如库存被别的消费者提前扣完),消费者需将 Redis 预减的库存加回,保证 Redis 与数据库最终一致。
  8. 超时处理:客户端轮询超过一定时间(如 30 秒)仍未生成订单,可提示失败。后台也可设置定时任务清理长时间未完成的“排队中”状态。

可选的简化与优化

  • 去掉 MQ:如果并发不是极高(如 < 5k QPS),可以在 Redis 预减后直接同步写数据库,但需做好数据库连接池和限流。
  • 轮询改进:可使用 Redis 的 list 或简单标记,轮询时直接查 Redis 中的状态(如 order:status:userId:goodsId),减少数据库压力。
  • 本地缓存标记:在秒杀服务内存中缓存“已售罄”标志,提前拦截请求。

如果需要更详细的文字解释或某个环节的具体代码实现(比如 Lua 脚本、幂等处理),请告诉我。