红包雨与入账峰值
→ 返回 高并发
春晚红包、App 红包雨、企业发福袋:短时 千万~亿级点击,每人金额小,但入账记录不能丢、不能重复领。介于「排行榜最终一致」与「抢票强一致库存」之间——钱/account 余额需准确,可接受百毫秒级延迟展示。
场景特征
| 特征 | 说明 |
|---|---|
| 写放大 | 一次活动 N 次「抢」请求 |
| 热点活动 | 单个 redpack:act:xxx 超级热 key |
| 金额守恒 | 总金额 = 已发出 + 剩余,不能超发 |
| 重复领取 | 同一用户只能成功一次 |
| 峰值短 | 分钟级结束 |
解决方案总览
活动配置预加载(总金额、份数、规则)
▼
抢红包:快速校验 + Redis 原子抢槽位
▼
成功 → 写领取记录(幂等)→ 异步入账(MQ)
▼
余额展示可延迟;账务以 DB 为准
1. 预分配与分片(削热 key)
避免单 Redis key 存「剩余金额」被万人抢:
| 方案 | 说明 |
|---|---|
| 红包拆桶 | 活动拆成 1024 个子红包队列,用户 hash(uid)%1024 路由 |
| 预先随机金额 | 子包内金额列表预生成,抢即 LPOP |
| CAS 扣份数 | remain_count 分桶 DECR |
-- 桶内剩余份数 DECR
local c = tonumber(redis.call('GET', KEYS[1]) or '0')
if c <= 0 then return 0 end
redis.call('DECR', KEYS[1])
return 12. 幂等与防重复
UNIQUE KEY uk_act_user (activity_id, user_id)SET grab:act:2024:uid:10001 1 NX EX 86400 # 抢到标记流程:先占位成功 → 再异步写账务;占位失败直接「已领取/手慢无」。
3. 入账异步(数据层外缓冲)
| 步骤 | 路径 |
|---|---|
| 抢成功 | 同步写 grab_log(或 Redis 队列) |
| 加余额 | MQ 消费者批量更新 wallet |
| 对账 | 活动结束 T+0 核对总金额 |
用户余额 UI:先展示「到账中」,消费成功后刷新(最终一致)。
4. 限流与降级
- 全局限流:活动 QPS 上限
- 单用户:1 秒 1 次
- 降级:抢光后静态返回,不再打 Redis
5. 与步数榜 / 抢票对照
| 红包雨 | 步数榜 | 抢票 | |
|---|---|---|---|
| 一致性 | 金额准确、可异步展示 | 可近似 | 座位强一致 |
| 热 key | 分桶必备 | 可按用户拉取 | 车次库存 |
| 失败语义 | 已抢完 | 榜更新慢 | 无票 |
检查清单
- 总金额是否可能超发?
- 重复点击是否重复入账?
- 单活动是否单 key?
- MQ 堆积时账务是否可对账修复?