去年参与某头部社交平台用户活跃度系统重构时,我们面临一个棘手问题:平台日活用户突破1.2亿后,传统的MySQL+Redis方案每天产生超过200GB的签到日志数据,内存占用和存储成本呈指数级增长。经过三个月技术攻关,我们最终将内存消耗降低到原有方案的1/50,这套方案后来在多个亿级产品中验证有效。
典型登录统计系统通常采用以下结构:
当用户量达到千万级时,这种设计暴露出三个致命缺陷:
我们确立了四个优化方向的技术指标:
经过对RoaringBitmap、HyperLogLog、BloomFilter等数据结构的基准测试,最终选择**位图(Bitmap)**作为核心数据结构,配合分层存储策略。这里特别要说明为什么没有选择看似更高级的HyperLogLog——虽然它的空间复杂度更低,但无法满足精确统计和连续签到的业务需求。
我们用365位的二进制位图表示用户全年签到状态,每位代表一天(1已签/0未签)。相比传统方案有质的飞跃:
具体实现代码示例:
python复制# 用户签到(设置第n位为1)
redis.bitfield('user:123:2023').set('u1', n, 1)
# 检查连续签到(BITPOS查找第一个0位)
continuous_days = redis.bitpos('user:123:2023', 0) - 1
为了进一步优化,我们设计了三级存储体系:
通过动态迁移策略,内存占用再降低60%。这里有个关键技巧:将用户ID哈希分片后,同一分片的多用户位图合并存储,利用CPU缓存行特性提升批量操作性能。
传统方案需要遍历每日记录,我们改用两种位运算技巧:
python复制def check_continuous(bits, days):
mask = (1 << days) - 1
return any((bits >> i) & mask == mask for i in range(365 - days))
实测表明,该算法在1亿用户量级下,查询性能提升400倍。
通过三项关键优化,我们将内存占用从理论值4.55GB降至1.2GB:
重要提示:Redis的BITCOUNT命令在大位图场景下会成为性能瓶颈,我们通过改写Redis模块,实现SIMD加速版BITCOUNT,性能提升8倍。
测试了三种备份方案后,最终选择混合策略:
| 方案 | RDB体积 | 恢复时间 | CPU消耗 |
|---|---|---|---|
| 全量位图 | 4.5GB | 25min | 高 |
| 差异位图 | 1.8GB | 12min | 中 |
| 列式存储 | 600MB | 8min | 低 |
每天凌晨采用差异备份+周全量备份的组合策略,备份窗口从6小时缩短至45分钟。
我们将该方案扩展到三个典型场景:
压测环境:32核/128GB内存 Redis集群
| 指标 | 传统方案 | 位图方案 | 提升倍数 |
|---|---|---|---|
| 签到QPS | 12万 | 210万 | 17.5x |
| 内存占用 | 240GB | 4.8GB | 50x |
| 连续签到计算延迟 | 45ms | 0.8ms | 56x |
| 备份体积 | 200GB/d | 4GB/d | 50x |
这套系统已稳定运行两年多,期间经历过三次春节流量高峰(峰值QPS 350万)的考验。最近我们正在尝试将位图方案与新一代硬件结合,比如使用Intel Optane持久内存存储冷位图,进一步降低TCO成本。