圆点 速度的未来将在一场在你所在的城市举办的活动中出现。

加入我们的 Redis 发布会

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 成员 — 返回成员在 ZSET 中的得分
ZRANGEZRANGE key-name start stop [WITHSCORES] — 返回成员以及排名在 start 和 stop 之间的成员的得分(可选)

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

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

了解一个 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 成员 — 返回成员在 ZSET 中的位置,成员按逆序排列
ZREVRANGEZREVRANGE key-name start stop [WITHSCORES] — 按 rank 从 ZSET 获取给定成员,成员按逆序排列
ZRANGEBYSCOREZRANGEBYSCORE key 最小值 最大值 [WITHSCORES] [LIMIT 偏移量 计数] — 获取介于 min 和 max 之间的成员
ZREVRANGEBYSCOREZREVRANGEBYSCORE key 最大值 最小值 [WITHSCORES] [LIMIT 偏移量 计数] — 按逆序获取介于 min 和 max 之间的成员
ZREMRANGEBYRANKZREMRANGEBYRANK key-name start stop ——从 ZSET 中移除处于 start 和 stop 之间排名的项目
ZREMRANGEBYSCOREZREMRANGEBYSCORE key-name min max ——从 ZSET 中移除处于 min 和 max 之间分数的项目
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 的项目的评分会被累加。>>> 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)]

提供不同的聚合很简单,但我仅限于求和、求最小值和求最大值。>>> 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)]

我们还可以将 SET 作为输入传递给 ZINTERSTORE 和 ZUNIONSTORE;它们的表现如同 ZSET,所有分数均等于 1。

ZSET 并集和交集乍看之下可能难以理解,因此让我们看看一些图,展示交集和并集过程中发生的事情。图 3.1 显示了两个 ZSET 的交集和最终的 ZSET 结果。在此情况下,我们的聚合是默认的求和,因此会累加分数。

与交集不同,当我们执行并集运算时,至少存在于输入 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 中存在的元素与 minimum 函数结合起来以生成 zset-u
图 3.3 当调用 conn.zunionstore(‘zset-u2’, [‘zset-1’, ‘zset-2’, ‘set-1’]) 时会发生什么;zset-1、zset-2 或 set-1 中存在的元素通过相加结合起来以生成 zset-u2

在第 7 章中,我们将 ZINTERSTOREZUNIONSTORE 作为几种不同类型的搜索的一部分。我们还将讨论多种将 ZSET 分数与可选的 WEIGHTS 参数结合起来的方法,以进一步扩展可通过 SETZSET 解决的问题类型。

在开发应用程序时,你可能会遇到一个名为发布/订阅的模式,也称为 pub/sub。Redis 包括此功能,我们会在后面介绍此功能。

2 分数实际上以 IEEE 754 浮点双精度数的形式存储在 Redis 里面。