dot Redis 8 已发布——并且它是开源的

了解更多

Redis 有序集合

返回术语表

Redis 中的有序集合是一种强大的数据结构,它结合了集合和有序列表的特性。它允许您存储唯一的元素集合,同时为每个元素分配一个分数或排名。该分数用于确定集合中元素的顺序,这使得有序集合成为需要有序数据的应用的绝佳选择。

在 Redis 中,有序集合是通过哈希表和跳跃列表数据结构的组合来实现的。哈希表根据元素的值提供快速访问,而跳跃列表根据元素的分数维护元素的排序顺序。这种双重结构使 Redis 能够有效地执行有序集合上的操作。

有序集合特性

有序集合的关键特性之一是能够动态地添加、删除或更新元素,同时保持其排序顺序。您可以将元素插入有序集合中并为其关联一个分数,该分数可以是浮点数。Redis 使用分数来确定元素在有序集合中的位置。如果具有相同值的元素已经存在,则会相应地更新其分数。

Redis 中有序集合的操作和功能

有序集合提供了各种操作来操纵数据。您可以根据元素的分数检索特定范围内的元素,从而实现高效的分页或排名功能。Redis 支持包含和不包含的范围查询,允许您按分数或在有序集合中的位置获取元素。此外,您还可以在有序集合上执行集合操作,如并集、交集和差集,从而组合或比较多个有序集合。

Redis 还提供了有效的方法来增加或减少有序集合中元素的分数。此功能在需要跟踪排名或维护排行榜的场景中特别有用。通过增加或减少元素的分数,您可以轻松更新其在有序集合中的位置,而无需进行复杂的操作。

有序集合的优势

Redis 中的有序集合提供了多项优势。得益于底层哈希表结构,它们提供了基于元素值的快速访问。跳跃列表确保了基于分数的有效排序和范围查询。有序集合广泛用于各种应用,包括排行榜、实时分析、作业调度等。

ZSET

让我们看一下表 3.9 中一些常用的 ZSET 命令。表 3.9 一些常用的 ZSET 命令

命令示例用法和描述
ZADDZADD key-name score member [score member …] — 将具有给定分数的成员添加到 ZSET
ZREMZREM key-name member [member …] — 从 ZSET 中移除成员,返回被移除的成员数量
ZCARDZCARD key-name — 返回 ZSET 中的成员数量
ZINCRBYZINCRBY key-name increment member — 增加 ZSET 中成员的分数
ZCOUNTZCOUNT key-name min max — 返回分数介于给定最小值和最大值之间的成员数量
ZRANKZRANK key-name member — 返回给定成员在 ZSET 中的位置
ZSCOREZSCORE key-name member — 返回成员在 ZSET 中的分数
ZRANGEZRANGE key-name start stop [WITHSCORES] — 返回排名介于 start 和 stop 之间的成员,可选地包括分数

我们在第 1 章和第 2 章中使用过其中一些命令,因此您应该已经很熟悉它们了。让我们快速回顾一下其中一些命令的用法。列表 3.9 显示 Redis 中一些常用 ZSET 命令的示例交互>>> conn.zadd(‘zset-key’, ‘a’, 3, ‘b’, 2, ‘c’, 1) 3

在 Python 中向 ZSET 添加成员的参数顺序与标准 Redis 相反,这使得顺序与 HASH 相同。>>> conn.zcard(‘zset-key’) 3

了解 ZSET 的大小在某些情况下可以告诉我们是否需要修剪我们的 ZSET。>>> conn.zincrby(‘zset-key’, ‘c’, 3) 4.0

我们也可以像对 STRING 和 HASH 值那样增加成员的分数。>>> conn.zscore(‘zset-key’, ‘b’) 2.0

如果我们一直维护计数器或排行榜,获取单个成员的分数可能会很有用。>>> conn.zrank(‘zset-key’, ‘c’) 2

通过获取成员的 0 索引位置,我们以后可以轻松使用 ZRANGE 获取值的范围。>>> conn.zcount(‘zset-key’, 0, 3) 2L

计算具有给定分数范围的项目的数量对于某些任务非常有用。>>> conn.zrem(‘zset-key’, ‘b’) True

删除成员就像添加成员一样容易。>>> conn.zrange(‘zset-key’, 0, -1, withscores=True) [(‘a’, 3.0), (‘c’, 4.0)]

为了调试,我们通常使用此 ZRANGE 调用获取整个 ZSET,但在实际用例中,通常会一次获取相对少量的一组项目。

您可能还记得我们在第 1 章和第 2 章中使用了 ZADD、 ZREM、 ZINCRBY、 ZSCORE 和 ZRANGE 命令,因此它们的语义应该不足为奇。 ZCOUNT 命令与其他命令略有不同,主要用于让您发现分数介于给定最小值和最大值之间的值的数量。

表 3.10 显示了 Redis 中您会觉得有用的更多 ZSET 命令。表 3.10 用于从 ZSET 中获取和删除数据范围以及提供类似 SET 的交集的命令

命令示例用法和描述
ZREVRANKZREVRANK key-name member — 返回成员在 ZSET 中的位置,成员按降序排列
ZREVRANGEZREVRANGE key-name start stop [WITHSCORES] — 按排名从 ZSET 中获取给定成员,成员按降序排列
ZRANGEBYSCOREZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] — 获取 min 和 max 之间的成员
ZREVRANGEBYSCOREZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] — 按降序获取 min 和 max 之间的成员
ZREMRANGEBYRANKZREMRANGEBYRANK key-name start stop — 移除排名介于 start 和 stop 之间的 ZSET 中的项目
ZREMRANGEBYSCOREZREMRANGEBYSCORE key-name min max — 移除分数介于 min 和 max 之间的 ZSET 中的项目
ZINTERSTOREZINTERSTORE dest-key key-count key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] — 对提供的 ZSET 执行类似 SET 的交集操作
ZUNIONSTOREZUNIONSTORE dest-key key-count key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] — 对提供的 ZSET 执行类似 SET 的并集操作

这是您第一次看到其中一些命令。如果某些 ZREV* 命令让您感到困惑,请记住它们的工作原理与其非反向对应的命令相同,只是 ZSET 的行为如同它是反向顺序(分数从高到低排序)。您可以在下面的列表中看到它们的一些用法示例。列表 3.10 显示 ZINTERSTORE 和 ZUNIONSTORE 的示例交互>>> conn.zadd(‘zset-1’, ‘a’, 1, ‘b’, 2, ‘c’, 3) 3 >>> conn.zadd(‘zset-2’, ‘b’, 4, ‘c’, 1, ‘d’, 0) 3

我们将首先创建几个 ZSET。>>> conn.zinterstore(‘zset-i’, [‘zset-1’, ‘zset-2’]) 2L >>> conn.zrange(‘zset-i’, 0, -1, withscores=True) [(‘c’, 4.0), (‘b’, 6.0)]

执行 ZINTERSTORE 或 ZUNIONSTORE 时,我们的默认聚合方式是求和,因此位于多个 ZSET 中的项目的分数会被相加。

虽然我们仅限于求和 (sum)、最小值 (min) 和最大值 (max),但提供不同的聚合方式很容易。>>> conn.zunionstore(‘zset-u’, [‘zset-1’, ‘zset-2′], aggregate=’min’) 4L >>> conn.zrange(‘zset-u’, 0, -1, withscores=True) [(‘d’, 0.0), (‘a’, 1.0), (‘c’, 1.0), (‘b’, 2.0)]

我们也可以将 SET 作为输入传递给 ZINTERSTORE 和 ZUNIONSTORE;它们的行为就像所有分数都等于 1 的 ZSET 一样。>>> conn.sadd(‘set-1’, ‘a’, ‘d’) 2 >>> conn.zunionstore(‘zset-u2’, [‘zset-1’, ‘zset-2’, ‘set-1’]) 4L >>> conn.zrange(‘zset-u2’, 0, -1, withscores=True) [(‘d’, 1.0), (‘a’, 2.0), (‘c’, 4.0), (‘b’, 6.0)]

ZSET 的并集和交集乍一看可能难以理解,所以我们来看一些图示,展示交集和并集过程中发生的情况。图 3.1 显示了两个 ZSET 的交集以及最终的 ZSET 结果。在本例中,我们的聚合方式是默认的求和 (sum),因此分数被相加。

与交集不同,当我们执行并集操作时,至少存在于一个输入 ZSET 中的项目会包含在输出中。图 3.2 显示了使用不同聚合函数 min 执行并集操作的结果,如果在多个输入 ZSET 中存在同一成员,则取其最小分数。

在第 1 章中,我们利用了可以将 SET 作为 ZSET 并集和交集操作的一部分的特性。此功能使我们能够轻松地从群组中添加和移除文章,而无需将分数和插入时间传播到额外的 ZSET 中。图 3.3 显示了 ZUNIONSTORE 调用,它将两个 ZSET 和一个 SET 组合生成最终的 ZSET

图 3.1 调用 conn.zinterstore(‘zset-i’, [‘zset-1’, ‘zset-2’]) 时发生的情况;同时存在于 zset-1 和 zset-2 中的元素被相加得到 zset-i
图 3.2 调用 conn.zunionstore(‘zset-u’, [‘zset-1’, ‘zset-2′], aggregate=’min’) 时发生的情况;存在于 zset-1 或 zset-2 中的元素使用 min 函数组合得到 zset-u
图 3.3 调用 conn.zunionstore(‘zset-u2’, [‘zset-1’, ‘zset-2’, ‘set-1’]) 时发生的情况;存在于 zset-1、zset-2 或 set-1 中的任何元素通过加法组合得到 zset-u2

在第 7 章中,我们将使用 ZINTERSTORE 和 ZUNIONSTORE 作为几种不同类型搜索的一部分。我们还将讨论几种不同的方法,如何将 ZSET 分数与可选的 WEIGHTS 参数结合,以进一步扩展可以使用 SET 和 ZSET 解决的问题类型。

在开发应用程序时,您可能遇到过一种称为发布/订阅的模式,也称为 pub/sub。Redis 包含了这项功能,我们接下来将进行介绍。

2 分数在 Redis 内部实际存储为 IEEE 754 双精度浮点数。