1. 项目背景与核心挑战
360万QPS的春节红包系统听起来像什么?就像在双十一零点让整个北京地铁站的人同时用手机抢红包。字节跳动这套系统支撑了抖音、头条等八大平台的红包活动,包括集卡、红包雨等十多种玩法,每天要处理现金红包、补贴券等各类奖励发放。
这个量级的系统面临四个致命问题:
- 资金安全:每秒360万次发放请求,如何避免超发一毛钱?
- 数据一致性:用户在抖音领的红包,切换到头条必须立即可见
- 热key压力:所有用户抢红包时都在读写同一个Redis计数器
- 系统容灾:十几个依赖服务中任意一个挂掉都不能影响核心流程
关键指标:系统峰值QPS 360万,红包发放与资金入账存在6倍处理能力差,需要1152个DB实例才能支撑同步入账
2. Token方案:资金与体验的平衡术
2.1 传统方案的死胡同
同步入账需要1152个DB实例,资源浪费堪比用航母运快递;纯异步方案又会让用户等待十几分钟才能看到金额,体验差得像网速2G时代。
2.2 字节的解法:金融级魔术
- Token生成:用Protobuf格式封装金额、活动ID等元信息(比JSON省50%空间)
- 双端存储:客户端立即展示,服务端记录待入账
- 余额计算:动态公式 = 已入账金额 + Token列表 - 处理中Token
- 提现强同步:用户提现时强制完成所有待入账操作
python复制# Token生成伪代码示例
def generate_token(user_id, amount, activity_id):
token = Protobuf(
version = "v1",
user_id = user_id,
amount = amount,
activity_id = activity_id,
timestamp = time.now(),
signature = hmac_sha256(secret_key)
)
redis.set(f"pending:{user_id}:{token.id}", token)
return token.encode()
实测数据:Protobuf编码使网络传输量减少52%,存储成本降低47%
3. 热Key优化:百万QPS的降压秘籍
3.1 常规方案为何失效
Key分片方案需要维护100个Redis计数器,读操作要聚合所有分片,相当于把拥堵的高速路改成100条乡道,反而更堵。
3.2 本地合并的精妙设计
- 写入阶段:每个服务实例用atomic.AddInt64本地累加
- 同步机制:每秒将增量通过单次incr同步到Redis
- 读取策略:直接返回本地缓存值,定时同步最新总数
go复制// Go语言实现示例
var localCounter int64
func AddRedPacket(amount int64) {
atomic.AddInt64(&localCounter, amount)
}
func SyncToRedis() {
ticker := time.NewTicker(1 * time.Second)
for range ticker.C {
delta := atomic.LoadInt64(&localCounter)
redis.IncrBy("total_counter", delta)
atomic.AddInt64(&localCounter, -delta)
}
}
效果对比:从直接访问Redis的360万QPS降到2000实例×1次/秒=2000QPS,降幅达99.94%
4. 容灾体系的26把钥匙
4.1 依赖分级管理
- 强依赖:TCC事务、MySQL存储(不可降级)
- 弱依赖:ByteKV、分布式锁(可降级)
4.2 关键降级策略
| 故障场景 | 降级措施 | 影响范围 |
|---|---|---|
| Redis超时 | 切到本地缓存 | 实时统计延迟1分钟 |
| 分布式锁失败 | 改用本地锁 | 可能少量重复发放 |
| RPC服务不可用 | 返回兜底数据 | 部分非核心功能不可用 |
容灾底线:极端情况下系统仅依赖Docker+MySQL仍可维持核心功能
5. 资金安全的三重门禁
5.1 防护层级
- 预算控制层:活动总预算消耗达90%时熔断
- 单笔拦截层:异常大额请求直接拒绝
- 原子扣减层:Redis incr保证库存操作原子性
5.2 对账矩阵
| 对账类型 | 频率 | 精度 | 补救措施 |
|---|---|---|---|
| 实时对账 | 每分钟 | 99.9% | 自动触发补发 |
| 小时级对账 | 每小时 | 99.99% | 人工审核+补发 |
| 日终对账 | 每日 | 100% | 财务调账 |
6. 实战避坑指南
- Token过期问题:设置7天有效期,过期自动入账,避免长期挂账
- 本地缓存雪崩:各实例同步时间错开±300ms,避免同时冲击Redis
- 降级开关陷阱:每个开关必须配套监控,避免降级后无人知晓
- 压测数据污染:使用特殊标记区分压测流量,自动清理测试数据
血泪教训:某次预发环境忘记打开降级开关演练,导致真实故障时手忙脚乱
7. 架构演进的启示
这套架构最精妙之处在于用"空间换时间"的哲学:
- Token方案用存储空间换用户体验
- 本地合并用内存换Redis吞吐量
- 降级开关用复杂度换系统可用性
对于中小规模系统,可以简化实现:
- 先用MySQL+本地缓存实现基础版Token方案
- 热key处理改用Redis集群+客户端缓存
- 资金安全至少实现预算控制和日终对账
这种架构不仅适用于红包系统,还能迁移到:
- 电商优惠券发放
- 游戏道具领取
- 积分奖励系统
最后分享一个监控技巧:在资金对账系统里设置"毛利润监控",当发现发放金额/核销金额比值异常时,往往能提前15分钟发现资金漏洞。