如何管理 Redis 中的 Sorted Sets 有序集合

源地址:https://www.digitalocean.com/community/cheatsheets/how-to-manage-sorted-sets-in-redis
作者:Mark Drake

简介

Redis 是一个开源的、运行在内存中的键值数据库。在 Redis 中,有序集合是一种与集合类似的数据类型,都是不重复的字符串组。不同的是,有序集的每个成员都与一个分数相关,允许它们从最小的分数到最大的分数进行排序或排名。

本教程解释了如何创建有序集,检索和删除其成员,以及基于现有的有序集创建新的有序集。

如何使用这个教程

这个教程是以小抄(Cheat Sheet)的形式写的,有对应的例子。我们鼓励你直接跳转到和你要完成的任务相关的章节进行阅读。

这个教程中的命令在 Ubuntu 18.04 中的 Redis 4.0.9 版本完成测试。如果你需要建设一个同样的环境,可以参考我们的 如何在 Ubuntu 18.04 上安装并加固 Redis 中的 Step 1 来安装 Redis。我们将通过 Redis 命令行界面redis-cli运行这些命令,来演示这些命令的行为。请注意,如果你使用不同的 Redis 工具--例如Redli —— 某些命令的实际输出可能有所不同。

此外,你还可以使用一个代管的 Redis 数据库实例来测试这些命令。但需要注意的是,根据你的数据库服务提供商的限制,这个教程中的某些命令可能运行效果和教程不一致。如果想要使用 DigitalOcean 提供的代管数据库,可以查看我们的代管数据库产品文档。使用代管 Redis 数据库实例时,你必须安装 Redli设置 TLS 隧道 来通过 TLS 链接到代管数据库。

创建有序集合并添加成员

要创建有序集合,请使用 zadd 命令。 zadd 接受将保存有序集的键的名称作为参数,后跟要添加的成员的分数和成员本身的值。以下命令将创建一个名为 faveGuitarists 的有序集合键,其中一个成员为 "Joe Pass",其得分为 1

127.0.0.1:6379>  zadd faveGuitarists 1 "Joe Pass"

zadd 将返回一个整数,表示如果成功创建有序集,则将多少个成员添加到有序集中。

输出

 (integer) 1

您可以使用 zadd 将多个成员添加到有序集中。注意他们的分数不需要是连续的,分数之间可以有间隔,并且在同一个有序集中的多个成员可以使用相同的分数:

127.0.0.1:6379>  zadd faveGuitarists 4 "Stephen Malkmus" 2 "Rosetta Tharpe" 3 "Bola Sete" 3 "Doug Martsch" 8 "Elizabeth Cotten" 12 "Nancy Wilson" 4 "Memphis Minnie" 12 "Michael Houser"

输出

 (integer) 8

zadd 可以接受以下选项,您必须在键名之后和第一个成员分数之前输入:

  • NXXX:这俩个选项互斥,在一次 zadd 操作当中,你只能选择一个:
    • NX:告诉 zadd 不能更新现有成员。使用此选项,zadd 只会添加新元素。
    • XX:告诉zadd只能更新现有元素。使用此选项,zadd 将永远不会添加新成员。
  • CHzadd 默认只返回添加到有序集中的新元素的数量。但是,如果包含此选项,zadd 将返回数量为 changed 的元素。这包括新添加的成员和分数发生变化的成员。
  • INCR:增加对应成员的粉质。如果该成员尚不存在,该命令会将其添加到已排序的集合中,并以默认值 0 及增量作为其分数。在包含 INCR 的情况下,zadd 如果成功,将返回成员的新分数。需要注意的是,如果使用了这个选项,zadd 只能同时增加一个成员。

除了使用 zadd 和对应的 INCR 选项,你还可以使用 zincrby 命令来实现同样的行为。zincrby 根据输入的分数增加对应成员的分数,而不是像 zadd 一样设定对应的分数。例如,以下命令将成员“Stephen Malkmus”(最初为“4”)的得分增加“5”到“9”。

127.0.0.1:6379>  zincrby faveGuitarists 5 "Stephen Malkmus"

输出

 "9"

zadd 命令的 INCR 选项一样,如果指定人员不存在,则 zincrby 将使用增量值作为其分数创建它。

从有序集合中提取成员

获取有序集中成员的最基本方法是使用 zrange 命令。这个命令接受要检索的有序集的键名,以及其中包含的成员的范围作为参数。该范围由从 0 开始的索引的两个数字定义,0 代表着集合中的第一个成员(或者分数最低的成员),1 代表下一个成员。

下面的这个示例展示了从 faveGuitarists 有序集中提取前四个成员:

127.0.0.1:6379>  zrange faveGuitarists 0 3

输出

1) "Joe Pass"
2) "Rosetta Tharpe"
3) "Bola Sete"
4) "Doug Martsch"

请注意,如果您传递给 zrange 的有序集有两个或多个共享相同分数的元素,它将按 字典序 或字母序对这些元素进行排序。

开始和停止索引也可以是负数,“-1”代表最后一个成员,“-2”代表倒数第二个,依此类推:

127.0.0.1:6379>  zrange faveGuitarists -5 -2

输出

1) "Memphis Minnie"
2) "Elizabeth Cotten"
3) "Stephen Malkmus"
4) "Michael Houser"

zrange 可以接受 WITHSCORES 参数,当包含该参数时,也会返回成员的分数:

127.0.0.1:6379>  zrange faveGuitarists 5 6 WITHSCORES

输出

1) "Elizabeth Cotten"
2) "8"
3) "Stephen Malkmus"
4) "9"

zrange 只能返回按数字升序排列的成员范围。如果希望按照降序返回成员范围必须使用 zrevrange 命令。你可以将这个命令看作在返回指定范围内的成员之前暂时反转给定有序集的顺序。因此,使用 zrevrange0 将代表键中的 最后的成员1 将代表倒数第二个,依此类推:

127.0.0.1:6379>  zrevrange faveGuitarists 0 5

输出

1) "Nancy Wilson"
2) "Michael Houser"
3) "Stephen Malkmus"
4) "Elizabeth Cotten"
5) "Memphis Minnie"
6) "Doug Martsch"

zrevrange 也接受 WITHSCORES 参数。

您可以使用 zrangebyscore 命令根据他们的分数返回一系列成员。在以下示例中,该命令将返回 faveGuitarists 键中的任何得分为 2、3 或 4 的成员:

127.0.0.1:6379>  zrangebyscore faveGuitarists 2 4

输出

1) "Rosetta Tharpe"
2) "Bola Sete"
3) "Doug Martsch"
4) "Memphis Minnie"

此示例中包含范围,这意味着它将返回得分为 2 或 4 的成员。您可以通过在范围的任一端加上左括号 (() 来排除范围的任何一端。以下示例将返回每个分数大于或等于“2”但小于“4”的成员:

127.0.0.1:6379>  zrangebyscore faveGuitarists 2 (4

输出

1) "Rosetta Tharpe"
2) "Bola Sete"
3) "Doug Martsch"

zrange 一样,zrangebyscore 可以接受 WITHSCORES 参数。它还接受 LIMIT 选项,您可以使用该选项仅从 zrangebyscore 输出中检索元素的选择。此选项接受一个 offset,它标记命令将返回的范围内的第一个成员,以及一个计数,它定义了多少个成员 命令将全部返回。例如,以下命令将查看 faveGuitarists 有序集的前六个成员,但只会从中返回 3 个成员,从范围中的第二个成员开始,由 1 表示:

127.0.0.1:6379>  zrangebyscore faveGuitarists 0 5 LIMIT 1 3

输出

1) "Rosetta Tharpe"
2) "Bola Sete"
3) "Doug Martsch"

zrevrangebyscore 命令根据他们的分数返回一个反向的成员范围。以下命令返回分数在 10 到 6 之间的集合中的每个成员:

127.0.0.1:6379>  zrevrangebyscore faveGuitarists 10 6

输出

1) "Stephen Malkmus"
2) "Elizabeth Cotten"

zrangebyscore 一样,zrevrangebyscore 可以接受 WITHSCORESLIMIT 选项。此外,您可以通过在其前面加上一个左括号来排除范围的任一端。

有时,有序集中的所有成员都具有相同的分数。在这种情况下,您可以使用 zrangebylex 命令强制 redis 返回按_字典顺序_或按字母顺序排序的一系列元素。要试用此命令,请运行以下 zadd 命令以创建一个有序集,其中每个成员具有相同的分数:

127.0.0.1:6379>  zadd SomervilleSquares 0 Davis 0 Inman 0 Union 0 porter 0 magoun 0 ball 0 assembly

zrangebylex 后面必须跟键名、开始间隔和停止间隔。开始和停止间隔必须以左括号 (() 或左括号 ([) 开头,如下所示:

127.0.0.1:6379>  zrangebylex SomervilleSquares [a [z

输出

1) "assembly"
2) "ball"
3) "magoun"
4) "porter"

请注意,此示例仅返回集合中八个成员中的四个,即使该命令查找从 az 的范围。这是因为 Redis 值区分大小写,因此以大写字母开头的成员被排除在其输出之外。要返回这些,您可以运行以下命令:

127.0.0.1:6379>  zrangebylex SomervilleSquares [A [z

输出

1) "Davis"
2) "Inman"
3) "Union"
4) "assembly"
5) "ball"
6) "magoun"
7) "porter"

zrangebylex 还接受特殊字符 -,表示负无穷,和 +,表示正无穷。因此,以下命令语法也将返回有序集的每个成员:

127.0.0.1:6379>  zrangebylex SomervilleSquares - +

请注意,zrangebylex 不能以相反的字典(字母升序)顺序返回已排序的集合成员。如果需要降序返回,需要使用 zrevrangebylex

127.0.0.1:6379>  zrevrangebylex SomervilleSquares + -

输出

1) "porter"
2) "magoun"
3) "ball"
4) "assembly"
5) "Union"
6) "Inman"
7) "Davis"

因为它适用于每个成员具有相同分数的有序集,所以 zrangebylex 不支持 WITHSCORES 选项。但是,它确实支持 LIMIT 选项。

获取关于有序集的信息

要找出给定有序集中有多少成员(或者换句话说,要确定其 cardinality),请使用 zcard 命令。以下示例显示了本指南第一部分中的 faveGuitarists 键中有多少成员:

127.0.0.1:6379>  zcard faveGuitarists

输出

 (integer) 9

zcount 可以告诉您在给定的有序集中有多少元素在分数范围内。键后面的第一个数字是范围的开始,第二个是范围的结束:

127.0.0.1:6379>  zcount faveGuitarists 3 8

输出

 (integer) 4

zscore 输出有序集中指定成员的分数:

127.0.0.1:6379>  zscore faveGuitarists "Bola Sete"

输出

 "3"

如果指定的成员或键不存在,zscore 将返回 (nil)

zrank 类似于 zscore,但它不是返回给定成员的分数,而是返回其排名。在 Redis 中,rank 是有序集成员的从零开始的索引,按其分数排序。例如,"Joe Pass" 的得分为 1,但因为这是键中任何成员的最低得分,所以它的排名为 0

127.0.0.1:6379>  zrank faveGuitarists "Joe Pass"

输出

 (integer) 0

还有另一个名为“zrevrank”的 Redis 命令,它执行与“zrank”相同的功能,但相反,它颠倒了集合中成员的排名。在以下示例中,成员“Joe Pass”的得分最低,因此反向排名最高:

127.0.0.1:6379>  zrevrank faveGuitarists "Joe Pass"

输出

 (integer) 8

成员的分数与其排名之间的唯一关系是他们的分数与其他成员的分数之间的关系。如果两个连续成员之间存在分数差距,则不会反映在他们的排名中。请注意,如果两个成员的分数相同,则按字母顺序排在第一位的成员排名较低。

zscore 一样,zrankzrevrank 如果键或成员不存在,将返回 (nil)

zlexcount 可以告诉您在一个字典范围之间的有序集中有多少成员。以下示例使用上一节中的“SomervilleSquares”有序集:

127.0.0.1:6379>  zlexcount SomervilleSquares [M [t

输出

 (integer) 5

该命令与 zrangebylex 命令的语法相同,因此请参阅上一节有关如何定义字符串范围的详细信息。

从有序集中移除成员

zrem 命令可以从有序集中删除一个或多个成员:

127.0.0.1:6379>  zrem faveGuitarists "Doug Martsch" "Bola Sete"

zrem 将返回一个整数,指示它从有序集中删除了多少成员:

输出

 (integer) 2

有三个 Redis 命令可让您根据范围删除已有序集的成员。例如,如果有序集中的每个成员具有相同的分数,您可以使用 zremrangebylex 根据词典范围删除成员。此命令使用与 zrangebylex 相同的语法。以下示例将从上一节中创建的 SomervilleSquares 键中删除所有以大写字母开头的成员:

127.0.0.1:6379>  zremrangebylex SomervilleSquares [A [Z

zremrangebylex 将输出一个整数,说明它删除了多少成员:

输出

 (integer) 3

您还可以使用 zremrangebyscore 命令根据分数范围删除成员,该命令使用与 zrangebyscore 命令相同的语法。以下示例将删除 faveGuitarists 中得分为 4、5 或 6 的每个成员:

127.0.0.1:6379>  zremrangebyscore faveGuitarists 4 6

输出

 (integer) 1

您可以使用 zremrangebyrank 命令根据一系列等级从集合中删除成员,该命令使用与 zrangebyrank 相同的语法。以下命令将删除有序集中具有最低排名的三个成员,这些成员由一系列从零开始的索引定义:

127.0.0.1:6379>  zremrangebyrank faveGuitarists 0 2

输出

 (integer) 3

请注意,传递给“zremrangebyrank”的数字也可以是负数,-1代表最高等级,-2代表下一个最高等级,依此类推。

从现有的有序集中创建一个新的有序集

Redis 包含两个命令,允许您比较多个有序集的成员并基于这些比较创建新的有序集:zinterstorezunionstore。要试验这些命令,请运行以下 zadd 命令来创建示例有序集。

127.0.0.1:6379>  zadd NewKids 1 "Jonathan" 2 "Jordan" 3 "Joey" 4 "Donnie" 5 "Danny"
127.0.0.1:6379>  zadd Nsync 1 "Justin" 2 "Chris" 3 "Joey" 4 "Lance" 5 "JC"

zinterstore 查找由两个或多个有序集共享的成员——它们的交集——并生成一个仅包含这些成员的新有序集。此命令必须按顺序包含相交成员将存储为有序集的目标键的名称、传递给“zinterstore”的键的数量以及您要分析的键的名称:

127.0.0.1:6379>  zinterstore BoyBands 2 NewKids Nsync

zinterstore 将返回一个整数,显示存储到目标有序集中的元素数量。因为 NewKidsNsync 只共享一个成员,"Joey",所以该命令将返回 1

输出

 (integer) 1

请注意,zinterstore 将覆盖目标键内容。

zunionstore 将创建一个新的有序集,其中包含传递给它的键的每个成员。此命令使用与 zinterstore 相同的语法,并且需要目标键的名称、传递给命令的键的数量以及键的名称:

127.0.0.1:6379>  zunionstore SuperGroup 2 NewKids Nsync

zinterstore 一样,zunionstore 将返回一个整数,显示存储在目标键中的元素数量。尽管两个原始有序集都有五个成员,因为有序集不能有重复成员,并且每个键都有一个名为“Joey”的成员,所以结果整数将是“9”:

输出

 (integer) 9

zinterstore 一样,zunionstore 将覆盖已存在的目标键的内容。

为了让您在使用 zinterstorezunionstore 创建新的有序集时更好地控制成员分数,这两个命令都接受 WEIGHTSAGGREGATE 选项。

WEIGHTS 选项后跟一个数字,表示命令中包含的每个有序集,它_weight_ 或乘以每个成员的分数。 WEIGHTS 选项后的第一个数字加权传递给命令的第一个键的分数,第二个数字加权第二个键,依此类推。

下面的示例创建一个新的有序集,其中包含来自 NewKidsNsync 有序集的相交键。它将“NewKids”键中的分数加权为 3 倍,并将“Nsync”键中的分数加权为 7 倍:

127.0.0.1:6379>  zinterstore BoyBandsWeighted 2 NewKids Nsync WEIGHTS 3 7

未指明 WEIGHTS 的情况下, zinterstorezunionstore 的权重默认为 1

AGGREGATE 接受三个子选项。其中第一个,SUM,通过在组合集中添加匹配成员的分数来实现 zinterstorezunionstore 的默认行为。

如果您对共享一个成员的两个有序集运行 zinterstorezunionstore 操作,但该成员在每个集合中具有不同的分数,则可以强制操作分配新集合中两个分数中较低的一个 MIN 子选项。

127.0.0.1:6379>  zinterstore BoyBandsWeightedMin 2 NewKids Nsync WEIGHTS 3 7 AGGREGATE MIN

因为这两个有序集只有一个匹配的成员具有相同的分数 (3),所以此命令将创建一个新集,其中的成员具有两个加权分数中较低的一个:

127.0.0.1:6379>  zscore BoyBandsWeightedMin "Joey"

输出

 "9"

同样,AGGREGATE 可以强制 zinterstorezunionstore 使用 MAX 选项分配两个分数中的较高者:

127.0.0.1:6379>  zinterstore BoyBandsWeightedMax 2 NewKids Nsync WEIGHTS 3 7 AGGREGATE MAX

此命令创建一个包含一个成员“Joey”的新集合,该集合具有两个加权分数中的较高者:

127.0.0.1:6379>  zscore BoyBandsWeightedMax "Joey"

输出

 "21"

WEIGHTS 视为一种在分析之前临时操纵成员分数的方法会很有帮助。同样,将 AGGREGATE 选项视为一种在将成员添加到新集合之前决定如何控制成员分数的方法是有帮助的。

总结

本指南详细介绍了一些用于在 Redis 中创建和管理有序集的命令。