秒杀与抢购

返回 高并发

电商秒杀(双 11、限量款、iPhone 首发)与 12306 抢票 同属 强一致库存,但链路更短、SKU 更碎、营销流量更不可预测。共性是不能超卖;差异是购物车弱、下单极快、失败率极高


场景特征

特征说明
瞬时流量活动开始秒级涌入
成功率极低库存千级、参与百万级
热点 SKU单商品库存 key 极热
羊毛党 / 脚本需风控、令牌、人机验证
体验「排队中」「已售罄」要明确快速

解决方案总览

活动页 CDN 静态化
      ▼
网关限流 + 风控(设备指纹、黑名单)
      ▼
秒杀令牌(排队页发放,持令牌才可下单)
      ▼
Redis 预扣库存(Lua 原子 DECR)或 DB 乐观锁
      ▼
创建订单(幂等)→ 支付超时 MQ 回滚库存

1. 页面与流量

手段说明
静态化商品介绍、规则页走 CDN,动态库存接口独立
按钮防重前端禁用连点 + 服务端幂等键
答题 / 验证码活动开始前过滤机器流量

2. 秒杀令牌(数据层外缓冲)

与 12306 购票令牌同族:

用户点击「立即抢购」
  → 进入排队(Redis 计数 + 随机延迟)
  → 成功则下发 token(JWT / 随机串,短 TTL 60s)
  → 仅带 token 的请求可访问下单 API
参数建议
令牌速率按库存与预估转化率反推,宁可少放不可打穿
TTL60~120 秒,过期需重新排队

3. 库存扣减

Redis 预扣(高并发首选)

-- KEYS[1]=stock:sku:1001  ARGV[1]=扣减数量
local s = tonumber(redis.call('GET', KEYS[1]) or '0')
if s < tonumber(ARGV[1]) then return 0 end
redis.call('DECRBY', KEYS[1], ARGV[1])
return 1

成功后再 同步写订单;失败直接返回「已售罄」。

数据库兜底

  • 订单表唯一约束 (user_id, activity_id) 防重复购买
  • 定时对账 Redis 库存与 DB remain

热点 SKU

手段说明
库存分桶stock:sku:1001:bucket:{0..7} 扣减后汇总
本地缓存「已售罄」库存为 0 后各节点短路,保护 Redis

4. 异步与降级

环节同步 / 异步
扣库存 + 创单同步
发短信、积分、推荐MQ 异步
支付超时延迟队列释放库存

过载时:关闭非核心接口,秒杀接口独立集群(舱壁)。


与 12306 对照

秒杀12306
查询量中(商品页)极高(余票)
排队常见必备
库存行SKU 级车次×席别×日期

检查清单

  • 是否存在「展示库存」与「扣减库存」混用?
  • 令牌发放速率是否低于下单处理能力?
  • 支付失败是否回滚库存?
  • 单 SKU 是否打爆单 Redis 节点?

相关