[TOC] #### 1. 前言 --- 100 个库存,10 万人同时抢,服务器 10 台,每台都能处理下单请求 怎么保证不多卖、不超卖 ?这是 Redis 最经典的实战场景之一 整体架构: ```plaintext 用户 → Nginx(限流)→ 应用服务 → Redis(库存扣减)→ MQ → MySQL(下单) ``` Redis 做前置判断和库存扣减,MySQL 做最终下单。不是所有请求都打到 MySQL,只有抢到的才走后面。 #### 2. 库存扣减 --- 用 decr 原子操作: ```bash set stock:2001 100 decr stock:2001 # 库存减 1,返回 99 decrby stock:2001 2 # 库存减 2,返回 97 ``` decr 是原子的,10 万个并发同时执行也不会超卖 返回值 < 0 说明库存不足: ```python result = redis.decr(f"stock:{product_id}") if result < 0: return "已售罄" ``` 不需要加锁,不需要分布式锁,decr 天然并发安全 #### 3. 防重复下单 --- 用 Set 记录已经抢过的用户: ```python if redis.sismember(f"seckill:{product_id}:users", user_id): return "你已经抢过了" ``` 抢成功后加入 Set: ```plaintext sadd seckill:2001:users user:1001 ``` #### 4. 完整流程 --- ```python def seckill(user_id, product_id): # 1. 防重复 ifredis.sismember(f"seckill:{product_id}:users", user_id): return "已抢过" # 2. 扣库存 stock=redis.decr(f"stock:{product_id}") if stock < 0: return "已售罄" # 3. 记录用户 redis.sadd(f"seckill:{product_id}:users", user_id) # 4. 发 MQ 异步下单 mq.send({"user_id": user_id, "product_id": product_id}) return "抢购成功,稍后查看订单" ``` 注意点: + 库存预热:秒杀开始前把库存加载到 Redis + 异步下单:Redis 只做扣减和判断,下单走 MQ 异步处理,削峰填谷 + 兜底方案:Redis 挂了降级到数据库,宁可慢一点也不能不卖 #### 5. 本文小结 --- 小总结: + decr 原子扣库存,不需要加锁 + Set 防重复下单 + Redis 做前置判断,下单走 MQ 异步 + 预热 + 异步 + 兜底,三道保障