登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

Redis Sorted Set构建实时排行榜

来源:17golang原创

时间:2026-11-15 11:28:07 239浏览 收藏

Redis Sorted Set 很适合做实时排行榜:把用户或选手作为 member,把积分、销量或热度作为 score,所有成员放进同一个 key。分数变化时直接更新,读取时按名次取出 Top N,不需要在应用层把整张榜单重新排序。

官方地址:https://redis.io/docs/latest/develop/

最小可用方案是:用 ZADD leaderboard:season-1 score member 写入分数,用 ZRANGE leaderboard:season-1 0 9 REV WITHSCORES 读取前十名,再用 ZRANK 和 ZSCORE 查询个人名次与分数。注意名次从 0 开始,同分成员按字典序排列。

先把排行榜建模成 Sorted Set

Sorted Set 的成员唯一,但每个成员都绑定一个浮点数分数。排行榜 key 可以按赛季、活动或业务周期隔离,例如 leaderboard:season-1。同一 member 再次写入时不是新增第二行,而是更新原来的 score。

Redis Sorted Set 中成员、分数和实时排行榜输出的结构说明图
图1:Redis Sorted Set 的排行榜结构说明图,展示成员、分数与排名输出的关系。

初始化数据可以直接使用 Redis CLI。下面的示例是命令写法,注释放在代码块外,避免把 CLI 输出误当成注释:

# 写入三名选手;相同 member 再执行会更新分数
ZADD leaderboard:season-1 128 alice
ZADD leaderboard:season-1 256 bob
ZADD leaderboard:season-1 192 carol

# 给 alice 加分时覆盖旧分数,最终分数变成 148
ZADD leaderboard:season-1 148 alice

如果业务中的分数是增量,例如每答对一题加 10 分,可使用 ZINCRBY。不要在应用层先读取旧分数再计算,否则并发更新时容易产生覆盖。

# 原子增加 alice 的分数;不存在的成员会从 0 开始
ZINCRBY leaderboard:season-1 10 alice

用 ZRANGE 读取 Top N

Redis 6.2 以后,推荐用 ZRANGE 组合 REV、LIMIT 和 WITHSCORES 完成倒序榜单查询。0 是第一条,结束位置包含在结果中,所以前十名是 0 9:

# 按 score 从高到低取前 10 名,并同时返回分数
ZRANGE leaderboard:season-1 0 9 REV WITHSCORES

如果要做分页,使用固定的偏移量和数量即可:

# 取第 11 到第 20 名;offset 是 10,count 是 10
ZRANGE leaderboard:season-1 0 -1 REV LIMIT 10 10 WITHSCORES

这类查询的复杂度是 O(log(N)+M),其中 N 是集合成员数,M 是返回数量。排行榜接口不要一次返回几十万条,页面只需要 Top N 时就把范围限制在需要的窗口。

查询个人名次与分数

榜单页面通常还要显示“我排第几”。倒序排行榜里,直接使用 ZREVRANK;如果你的客户端只暴露了统一的 ZRANK 选项,也要明确使用倒序参数,避免把低分第一名误当成高分第一名。

# 查询 alice 在高分到低分榜单中的 0-based 名次
ZREVRANK leaderboard:season-1 alice

# 查询 alice 的当前分数
ZSCORE leaderboard:season-1 alice

返回名次后,展示层一般加 1 转成人类习惯的名次;不要直接把 Redis 的 0-based 值显示为“第 0 名”。成员不存在时,排名和分数都是空结果,接口应按“暂无排名”处理,而不是把空值转换成 0。

Redis Sorted Set 中 Top N 查询与个人名次查询的关系说明图
图2:排行榜查询结构说明图,区分 Top N、个人名次和分数查询。

同分、清理和接口边界

Sorted Set 中 member 唯一;当多个成员的 score 相同时,Redis 会按 member 的字典序排列。因此如果产品要求“同分按到达时间排序”,不能只把业务分数原样写入,需要把时间或序号编码进可比较的 score,或者额外维护一个可解释的并列规则。编码时要注意浮点精度,不要把过大的整数直接当作可精确比较的浮点数。

如果排行榜只保留前 10000 名,可以在写入后删除尾部成员。删除命令的结束位置同样是包含关系:

# 保留 rank 0 到 9999,删除从第 10000 名开始的成员
ZREMRANGEBYRANK leaderboard:season-1 10000 -1

生产接口建议把榜单 key 与赛季、租户或活动 ID 组合,避免不同业务共用 key;把读取范围限制在接口需要的窗口;对空榜单和不存在 member 返回明确的业务状态。需要按分数区间筛选时,优先使用 ZRANGE ... BYSCORE,不要先读取全部成员再由应用过滤。

常见问题

为什么排行榜名次从 0 开始?

Redis 返回的是从 0 开始的 rank,这是数组式索引。展示给用户时使用 rank + 1,但分页计算仍按 0-based 范围处理。

同一个用户可以在一个 Sorted Set 里出现两次吗?

不可以。member 是唯一的;再次 ZADD 会更新该 member 的 score。如果需要保留同一用户的多次成绩,应把“用户 ID + 记录 ID”组成不同 member,并另存用户维度的最新成绩。

小结:Sorted Set 负责按 score 维护有序成员,ZADD/ZINCRBY 负责写入,ZRANGE ... REV LIMIT 负责 Top N,ZREVRANK/ZSCORE 负责个人信息。把同分规则、空结果和榜单上限提前定义好,实时排行榜就能保持简单可控。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>