P15 Redis 如何实现上亿用户实时积分排行榜
面试题:上亿用户,积分实时变化,要求实时展示 TOP 排行,怎么实现?
推荐方案:Redis ZSet(有序集合)
text
key:rank:score
member:userId
score:积分更新积分(原子累加):
bash
ZINCRBY rank:score 100 10001查某用户排名(从 0 开始,+1 是第几名):
bash
ZREVRANK rank:score 10001查 Top N:
bash
ZREVRANGE rank:score 0 99 WITHSCORES # 前 100 名查指定排名区间的用户:ZREVRANGE rank:score 100 199。
为什么用 ZSet
- 跳表 + 哈希表实现:更新 O(logN)、查询排名 O(logN);
- 所有操作在 Redis 内存中完成,实时性极好;
- 天然支持"按分数排序",正序倒序都行。
大规模优化点
1. 分桶(分段排名)
一亿用户放一个 ZSet,key 过大、内存集中。方案:
text
按积分段分桶:rank:0-100、rank:100-1000 ...- 更新时直接落入对应桶;
- 查总排名:先算高于自己积分段的用户总数(各桶 SCARD 累加),再算本桶内名次;
- 取 Top N:从最高分段桶往下取,拼够 N 个。
2. 分数设计
积分是整数时,排名按积分即可。需要先到先得等规则时,可以用"积分×权重 + 时间戳"合成 score,或维护二级排序。
3. 一致性
- 积分变化高并发:用 Lua 脚本原子执行"加分 + 更新排行榜",避免重复累加;
- 排行榜与业务库数据最终一致:异步同步,允许短暂延迟;
- 榜单缓存:Top N 结果缓存几秒,避免每次查询都扫 ZSet。
4. 数据持久化
- 开启 RDB/AOF 防止重启丢榜;
- 榜单数据可以定期快照到 DB,供活动结算使用。
加分点
- 对比方案:DB 每次 order by limit 查询,百万级数据就扛不住了,ZSet 才是实时榜单标配;
- 活动型榜单按活动维度建 key,活动结束删除;
- 查询"我的排名 + 前后 10 名"用
ZRANK/ZREVRANGE组合。
一句话总结
积分排行榜用 Redis ZSet(member=用户、score=积分),ZINCRBY 更新、ZREVRANGE 取 TopN、ZREVRANK 查排名;超大规模按积分段分桶 + 桶内名次累加实现。