首页 / Redis 入门教程 / 有序集合 Sorted Set

Redis 入门教程

有序集合 Sorted Set

本教程共 40 篇 · 第 10 篇 · 更新于 2026-08-02

redissorted-set有序集合ZADDZRANGE排行榜延迟队列

10. 有序集合 Sorted Set

本节目标

  • 理解 Sorted Set 是「成员唯一 + 按分数 score 排序」的集合
  • 掌握写入与读取:ZADD / ZRANGE / ZREVRANGE / ZSCORE / ZRANK
  • 掌握按分数区间查询:ZRANGEBYSCORE / ZCOUNT / ZRANGEBYLEX
  • 掌握原子增减:ZINCRBY,以及交集并集 ZINTERSTORE / ZUNIONSTORE
  • 用排行榜、延迟队列等场景理解 Sorted Set 的用武之地

Sorted Set(有序集合)在 Set「成员唯一」的基础上,给每个成员关联了一个 double 类型的分数(score)。Redis 根据分数从小到大自动为成员排序,因此你可以随时按排名、按分数区间取数据。它同时具备「集合去重」和「列表有序」的优点,是最适合做排行榜的数据类型。

几个要点:

  • 成员(member)唯一,但分数(score)可以重复——多个成员可以并列同一分数,此时按 member 的字典序排序。
  • 底层编码为 listpack(小数据)或 skiplist + hashtable(大数据),查询、插入、删除平均 O(log N)。
  • 插入、更新分数都会自动重排,无需应用层维护顺序。

写入与按排名读取

ZADD 添加成员和分数:

命令:

127.0.0.1:6379> ZADD leaderboard 100 alice 200 bob 150 carol 200 dave

输出:

(integer) 4

返回值表示新加入的成员数(已存在的成员若分数变化属于「更新」,不计入新增)。注意 bobdave 分数都是 200,并列。

按排名区间取成员用 ZRANGE(从小到大,0 是第一名)。加 WITHSCORES 可同时返回分数:

命令:

127.0.0.1:6379> ZRANGE leaderboard 0 -1 WITHSCORES

输出:

1) "alice"
2) "100"
3) "carol"
4) "150"
5) "bob"
6) "200"
7) "dave"
8) "200"

倒序取(从高到低)用 ZREVRANGE

命令:

127.0.0.1:6379> ZREVRANGE leaderboard 0 2 WITHSCORES

输出:

1) "bob"
2) "200"
3) "dave"
4) "200"
5) "carol"
6) "150"

取某个成员的分数与排名:ZSCORE / ZRANK(升序排名,从 0 起)/ ZREVRANK(降序排名):

命令:

127.0.0.1:6379> ZSCORE leaderboard bob
127.0.0.1:6379> ZRANK leaderboard carol

输出:

"200"
(integer) 2

按分数区间查询

ZRANGEBYSCORE 按分数范围返回成员,-inf / +inf 表示无穷:

命令:

127.0.0.1:6379> ZRANGEBYSCORE leaderboard 150 200 WITHSCORES

输出:

1) "carol"
2) "150"
3) "bob"
4) "200"
5) "dave"
6) "200"

只统计某个分数段有多少人用 ZCOUNT

命令:

127.0.0.1:6379> ZCOUNT leaderboard 150 200

输出:

(integer) 3

按「字典区间」查询用 ZRANGEBYLEX(要求所有成员分数相同,否则结果无意义),适合做范围分页:

命令:

127.0.0.1:6379> ZRANGEBYLEX lexset [a [c

输出:

1) "alice"
2) "bob"
3) "carol"

提示:自 Redis 6.2 起,官方推荐用统一的 ZRANGE 语法配合 BYSCORE / BYLEX 选项来替代独立的 ZRANGEBYSCOREZRANGEBYLEX(旧的命令为了兼容仍可使用,但文档优先推荐新写法)。例如 ZRANGE leaderboard 150 200 BYSCORE 等价于 ZRANGEBYSCORE。Redis 8.x 继续沿用这一推荐。

原子增减与交集/并集

给某个成员的分数加增量用 ZINCRBY——它做原子自增,是排行榜实时加分的核心:

命令:

127.0.0.1:6379> ZINCRBY leaderboard 50 alice
127.0.0.1:6379> ZSCORE leaderboard alice

输出:

"150"
"150"

alice 从 100 加到了 150。

多集合之间也能做加权交集/并集,并写回新 key:ZINTERSTORE / ZUNIONSTORE。例如把「本周榜」和「历史总榜」合并成总积分:

命令:

127.0.0.1:6379> ZUNIONSTORE total 2 weekRank allRank WEIGHTS 1 2

输出:

(integer) 4

WEIGHTS 给每个源集合的分数乘上权重后相加,非常灵活。

实战场景

场景一:游戏 / 活动排行榜。 玩家得分用 ZADD,加分用 ZINCRBY,取 Top N 用 ZREVRANGE 0 9,取某玩家名次用 ZREVRANK,全部 O(log N),轻松支撑实时榜单。

场景二:延迟队列( Delayed Queue)。 把「任务 ID」作为 member、把「应执行的时间戳」作为 score,ZRANGEBYSCORE key -inf <now> LIMIT 0 N 取出到期任务处理。多个 worker 抢任务时配合 ZREM 或 Lua 保证只被一个处理。这是轻量延迟任务的经典做法。

场景三:热度 / 权重排序。 把文章、商品按热度分排序展示;用 ZUNIONSTORE 把「点击」「收藏」「评论」按不同权重合成一个综合热度分。

常见误区

  • 误区:score 必须是整数。 score 是 double,ZINCRBY 也支持浮点增量(如 ZINCRBY board 0.5 x)。
  • 误区:并列同分顺序随机。 同分成员按 member 字典序排列,是确定的,不是随机。
  • 误区:ZADD 只能新增。 ZADD 对已存在 member 会更新其分数并重排,返回 0(不算新增);若只想在不存在时插入,可用 NX / XX 选项。
  • 误区:ZRANGEBYSCORE 参数顺序无所谓。 必须是「小分数 大分数」,写成 (200 表示「小于 200 不含 200」的开区间写法也支持。

ZADD 进阶选项与分数注意点

ZADD 还提供几个实用的修饰选项,能覆盖不少并发场景:NX 仅在 member 不存在时才添加;XX 仅在已存在时才更新;GT 仅当新分数大于现有分数才更新;LT 仅当新分数小于现有分数才更新;CH 让返回值表示「发生变化的成员数(含新增与更新)」而不是只算新增数。利用 GT / LT,可以在多个客户端同时写分时实现「分数只升不降」或「只降不升」,避免一条迟到的旧请求把新分数冲掉。

关于分数本身有两点提醒:第一,score 是双精度浮点数,存在精度上限,极大或极精细的小数可能丢失末尾精度,不要拿它做要求完全精确的小数比较;第二,当多个 member 同分时,Redis 按 member 的字典序稳定排序,这个顺序是确定的、不是随机的,可以通过人为设计 member 命名来影响并列时的先后。把 Sorted Set 当作延迟队列时,建议 score 用「未来执行时间戳」,member 携带任务 ID 保证唯一,避免同分带来的顺序歧义。

另一个常见组合是配合 ZCARD(取成员总数)、ZLEXCOUNT(字典区间计数)做分页与统计。当排行榜需要跨周期(日/周/总榜)时,用不同 key(如 leaderboard:20260802)隔离每日数据,再用 ZUNIONSTOREWEIGHTS 跨天合并,就能灵活支持「日榜、周榜、总榜」多种视图,而无需在应用层重新汇总。

删除也是常见操作:按排名区间删用 ZREMRANGEBYRANK,按分数区间删用 ZREMRANGEBYSCORE,按字典区间删用 ZREMRANGEBYLEX,这些在「清理过期榜单、裁剪历史区间」时很方便。另外,ZRANGE / ZREVRANGE 都支持 LIMIT offset count,可以配合做「翻页」——但要注意基于排名的翻页在频繁写入时可能出现「同一元素翻到两页」的偏移漂移,对强一致翻页需求应改用基于游标或分数游标的方案。

小结

  • Sorted Set 是「成员唯一 + 按 score 排序」的集合,适合一切排行榜与带权重排序。
  • 写入/读取:ZADD / ZRANGE / ZREVRANGE / ZSCORE / ZRANK / ZREVRANK
  • 区间:ZRANGEBYSCORE / ZCOUNT / ZRANGEBYLEX(或统一用 ZRANGE ... BYSCORE/BYLEX)。
  • 增减/聚合:ZINCRBY / ZINTERSTORE / ZUNIONSTORE(支持 WEIGHTS)。
  • 用武之地:排行榜、延迟队列、综合热度排序。

提示:关于版本——本教程正文统一以 Redis 8.x(最新稳定版)为准;官方 redis.io 下载页另标 8.8 为 “Latest stable”,而 GitHub 上 redis/redis 的最新发布 tag 为 8.10.0,二者同属 8.x,命令与类型差异对教材影响极小。