Redis实现排行榜功能的核心是ZSet,它支持自动排序、去重、范围查询和原子更新,适用于游戏积分榜等实时高并发场景,基本操作包括ZADD、ZREVRANGE、ZREVRANK、ZINCRBY和ZREM,并需通过分片、清理、缓存等策略优化性能。

Redis 实现排行榜功能,核心是利用 ZSet(有序集合) 这一原生数据结构。它天然支持按分数自动排序、去重、范围查询和原子更新,非常适合实时、高并发的榜单场景,比如游戏积分榜、直播热度榜、电商销量排行等。
用 ZSet 实现排行榜的基本操作
-
添加或更新分数(ZADD)
ZADD leaderboard 1250.5 "player:1001"- 同一个 member 多次执行 ZADD,会自动覆盖旧 score,无需判断是否存在,天然幂等
- score 支持 double 类型,可精确到小数点后多位(如
1250.5001),避免分数冲突 - Java 中推荐使用 Lettuce 客户端(线程安全、响应式、连接复用),例如:
redisClient.zAdd("leaderboard", ScoredValue.of(1250.5, "player:1001")).block();
-
获取指定排名区间(ZREVRANGE)
ZREVRANGE leaderboard 0 9 WITHSCORES- 按分数从高到低返回前 10 名,下标从 0 开始
- 加
WITHSCORES可同时拿到 member 和 score,便于前端展示 - 分页查第 101~110 名:
ZREVRANGE leaderboard 100 109 WITHSCORES - 时间复杂度 O(log N + M),N 是总成员数,M 是返回数量,百万级数据也能毫秒响应
-
查某用户当前排名(ZREVRANK)
ZREVRANK leaderboard "player:1001"- 返回该用户在降序榜中的索引(0 表示第 1 名)
- 若需显示“并列第 3 名”,可配合
ZSCORE和ZCOUNT:先取其分数,再统计 ≥ 该分数的成员数
-
累加分数(ZINCRBY)
ZINCRBY leaderboard 50 "player:1001"- 适合点赞、积分增长类场景,原子性保障,无需读-改-写
-
删除用户(ZREM)
ZREM leaderboard "player:1001"
Redis Skill - 高性能缓存管理下载Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 用户注销、作弊封禁等场景直接移除,不影响其余排序
高并发下的性能优化策略
-
避免单热点 Key
- 千万级用户共用一个 ZSet 容易打满单节点带宽与内存
- 推荐按维度分片:如按地区(
leaderboard:cn/leaderboard:us)、按时间(leaderboard:20260806)、按业务类型(leaderboard:game_a/leaderboard:game_b) - 分片后可通过
ZUNIONSTORE或应用层聚合做全局视图(慎用,开销较大)
-
控制 ZSet 规模,定期清理
- 设置过期时间:
EXPIRE leaderboard 86400,自动清理过期榜单 - 删除低分用户:
ZREMRANGEBYSCORE leaderboard -inf 100,保留前 N 名或达标用户 - 避免全量遍历:不要用
ZREVRANGE leaderboard 0 -1查全部,应始终带范围参数
- 设置过期时间:
-
读写分离与缓存协同
- 热点榜单(如 Top 100)用本地缓存(Caffeine)+ 定时刷新(如每 5 秒拉一次
ZREVRANGE) - 单用户排名查询高频时,可对
ZREVRANK结果加短 TTL 缓存(如 30 秒),减少 Redis 穿透
- 热点榜单(如 Top 100)用本地缓存(Caffeine)+ 定时刷新(如每 5 秒拉一次
-
分数设计增强稳定性
- 并列时默认 rank 相同,但若需稳定排序(相同分时按提交先后),可将时间戳嵌入 score 小数部分:
score = baseScore + (1 - System.currentTimeMillis() / 1e13)
这样分数越高 + 时间越早 → 排名越靠前
- 并列时默认 rank 相同,但若需稳定排序(相同分时按提交先后),可将时间戳嵌入 score 小数部分:
-
客户端与连接层优化
- 使用连接池(Lettuce 默认支持),避免频繁建连
- 批量操作用 Pipeline:如一批玩家更新分数,打包发送减少网络往返
- 写请求可异步化(Lettuce 的 Mono/Flux),不阻塞主线程
不建议的做法
- 不要用 MySQL 或其他关系库做实时排序:UPDATE + ORDER BY 在高并发下极易锁表、慢查询
- 不要自己维护 List 或 SortedList 做排序:缺乏原子性,多线程竞争下排名错乱风险高
- 不要在应用层对全量 ZSet 数据做二次排序或统计:违背 Redis 设计初衷,性能断崖下降
Redis 的 ZSet 是经过生产验证的排行榜首选方案,关键不在“能不能用”,而在“怎么用得稳、用得省”。合理分片、控制规模、善用原子命令、搭配轻量缓存,就能轻松支撑百万级 QPS 的实时榜单。


















