Redis有序集合:高效管理与优化指南
引言
Redis是一个开源的、内存中的键值数据存储库。在Redis中,有序集合(Sorted Sets)是一种类似于集合的数据类型,两者都是由字符串组成的非重复组。区别在于有序集合的每个成员都与一个分数相关联,使得它们可以根据分数从最小到最大进行排序。与集合类似,有序集合中的每个成员必须是唯一的,尽管多个成员可以共享相同的分数。
本教程将解释如何创建有序集合,检索和删除其成员,并从现有集合中创建新的有序集合。
如何使用本指南
本指南以备忘单的形式编写,包含自包含的示例。我们鼓励您直接跳转到与您正在尝试完成的任务相关的任何部分。
本指南中显示的命令,已在运行Redis版本6.0.16的Ubuntu 22.04服务器上经过测试。要设置类似的环境,您可以按照我们关于如何在Ubuntu 22.04上安装和保护Redis的指南的第一步操作。我们将通过使用Redis命令行界面redis-cli
来演示这些命令的行为。如果您使用的是不同的Redis接口,例如Redli,某些命令的确切输出可能会有所不同。
作为另一种选择,您可以提供一个托管的Redis数据库实例来测试这些命令。但是,根据您的数据库提供者所允许的控制级别,本指南中的某些命令可能无法按照描述来执行。要提供一个Silicon Cloud托管数据库,请参考我们的托管数据库产品文档。然后,您需要安装Redli或者设置一个TLS隧道,以便通过TLS连接到托管数据库。
创建有序集合并添加成员
要创建一个有序集合,请使用ZADD
命令。ZADD
命令的参数包括持有有序集合的键名、要添加的成员的分数以及成员本身的值。下面的命令将创建一个名为faveGuitarists
的有序集合键,其中包含一个分数为1的成员“Joe Pass”。
- 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
可以接受以下选项,在输入键名之后、第一个成员分值之前必须输入:
- NX 或 XX:这两个选项效果相反,因此您在任何
ZADD
操作中只能包含其中一个:- NX:告诉
ZADD
不要更新现有成员。使用此选项,ZADD
将只添加新元素。 - XX:告诉
ZADD
只更新现有元素。使用此选项,ZADD
将永远不会添加新成员。
- NX:告诉
- CH:通常,
ZADD
只返回添加到有序集合的新元素数量。但如果包含此选项,ZADD
将返回更改的元素数量。这包括新添加的成员和分数被更改的成员。 - INCR:这会使命令增加成员的分数值。如果成员尚不存在,命令将以增量值作为其分数将其添加到有序集合中,就像其原始分数为0一样。如果包含
INCR
,ZADD
成功时将返回成员的新分数。请注意,使用此选项时,您一次只能包含一个分数/成员对。
不需要将INCR
选项传递给ZADD
,您可以使用ZINCRBY
命令来达到完全相同的效果。与ZADD
不同,ZINCRBY
不会赋予有序集合成员与分数值相对应的值,而是将该成员的分数增加相应的值。例如,以下命令将原本为4的成员“Stephen Malkmus”的分数增加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
这是文章《如何在Redis中管理已排序集合》的第2部分(共6部分)。
1) "Joe Pass" 2) "Rosetta Tharpe" 3) "Bola Sete" 4) "Doug Martsch"
请注意,如果您向 zrange
函数传递的有序集合中有两个或更多的元素具有相同的分数,它将按照字典顺序或字母顺序对这些元素进行排序。
起始和结束索引也可以是负数,-1
代表最后一个成员,-2
代表倒数第二个成员,依此类推。
- zrange faveGuitarists -5 -2
1) "Memphis Minnie" 2) "Elizabeth Cotten" 3) "Stephen Malkmus" 4) "Michael Houser"
zrange
可以接受 WITHSCORES
参数,当包含该参数时,还会返回成员的分数。
- zrange faveGuitarists 5 6 WITHSCORES
1) "Elizabeth Cotten" 2) "8" 3) "Stephen Malkmus" 4) "9"
zrange
只能按照升序返回一定范围的成员。要反转顺序并按降序返回一定范围,必须使用 zrevrange
命令。可以将这个命令视为在返回指定范围内的成员之前临时反转给定有序集合的顺序。因此,使用 zrevrange
,0
代表键中保存的最后一个成员,1
代表倒数第二个成员,以此类推。
- 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的所有成员。
- zrangebyscore faveGuitarists 2 4
1) "Rosetta Tharpe" 2) "Bola Sete" 3) "Doug Martsch" 4) "Memphis Minnie"
在这个示例中,范围是包含的,意味着它将返回得分为2或4的成员。您可以在范围的任一端之前添加开口括号(()
,以排除该端。以下示例将返回得分大于或等于2,但小于4的每个成员。
- zrangebyscore faveGuitarists 2 (4
1) "Rosetta Tharpe" 2) "Bola Sete" 3) "Doug Martsch"
与 zrange
一样,zrangebyscore
可以接受 WITHSCORES
参数。它也接受 LIMIT
选项,您可以使用它来从 zrangebyscore
的输出中仅检索部分元素。该选项接受一个偏移量,标记命令将返回的范围中的第一个成员,以及一个计数,定义命令将总共返回多少个成员。例如,以下命令将查看 faveGuitarists
排序集的前六个成员,但仅返回其中三个成员,从范围中的第二个成员开始,表示为1:
- zrangebyscore faveGuitarists 0 5 LIMIT 1 3
1) "Rosetta Tharpe" 2) "Bola Sete" 3) "Doug Martsch"
zrevrangebyscore
命令根据成员的分数返回一个反向范围。以下命令返回分数在10和6之间的集合中的每个成员。
- zrevrangebyscore faveGuitarists 10 6
这是文章《如何在Redis中管理已排序集合》的第3部分(共6部分)。
1) “Stephen Malkmus” 2) “Elizabeth Cotten”
与zrangebyscore
类似,zrevrangebyscore
可以接受WITHSCORES
和LIMIT
选项。此外,您可以使用开放括号(
使范围的任一端排除在外。
有时,一个有序集合中的所有成员都可能具有相同的分数。在这种情况下,您可以使用zrangebylex
命令强制Redis按照字典顺序或字母顺序返回一系列元素。为了尝试这个命令,请运行以下zadd
命令来创建一个每个成员都具有相同分数的有序集合:
- zadd SomervilleSquares 0 Davis 0 Inman 0 Union 0 porter 0 magoun 0 ball 0 assembly
zrangebylex
命令必须跟随一个键的名称、起始区间和结束区间。起始和结束区间必须以开放括号(
或开放方括号[
开头,如下所示:
- zrangebylex SomervilleSquares [a [z
1) “assembly” 2) “ball” 3) “magoun” 4) “porter”
请注意,即使命令请求了从a到z的范围,但是这个示例只返回了集合中的四个成员,这是因为Redis的值是区分大小写的,所以以大写字母开头的成员被排除在输出之外。要返回这些成员,您可以运行以下命令:
- zrangebylex SomervilleSquares [A [z
1) “Davis” 2) “Inman” 3) “Union” 4) “assembly” 5) “ball” 6) “magoun” 7) “porter”
zrangebylex
函数还可以接受特殊字符-
,表示负无穷,以及+
,表示正无穷。因此,以下命令语法也会返回有序集合的每个成员。
- zrangebylex SomervilleSquares – +
请注意,zrangebylex
无法按照逆字典顺序(降序排序)返回有序集合成员。如果需要按照逆字典顺序排序,请使用zrevrangebylex
。
- zrevrangebylex SomervilleSquares + –
1) “porter” 2) “magoun” 3) “ball” 4) “assembly” 5) “Union” 6) “Inman” 7) “Davis”
由于zrangebylex
仅用于具有相同分数的有序集合,因此不接受WITHSCORES
选项。然而,它接受LIMIT
选项。
获取有关排序集合的信息
要找出给定排序集合中有多少个成员(换句话说,确定它的基数),请使用zcard
命令。以下示例显示了在本指南的第一部分中的faveGuitarists
键中有多少个成员。
- zcard faveGuitarists
(integer) 9
ZCOUNT
命令可以告诉您在给定的有序集合中,有多少个元素的分数落在某个指定范围内。关键字后面的第一个数是范围的起始值,第二个数是范围的结束值。
- zcount faveGuitarists 3 8
(integer) 4
zscore
函数输出一个已排序集合中指定成员的分数。
- zscore faveGuitarists “Bola Sete”
这是文章《如何在Redis中管理已排序集合》的第4部分(共6部分)。
如果指定的成员或键不存在,zscore
将返回 (nil)
。
zrank
和 zscore
类似,但它不返回给定成员的分数,而是返回其排名。在 Redis 中,排名是有序集合成员的从零开始的索引,根据其分数进行排序。例如,“Joe Pass”的分数为1,但因为这是键中任何成员的最低分数,它的排名为0。
127.0.0.1:6379> zrank faveGuitarists "Joe Pass"
(integer) 0
还有一个 Redis 命令叫做 zrevrank
,它的功能和 zrank
相同,只是将集合中的成员的排名进行了反转。在下面的示例中,成员“Joe Pass”的得分最低,因此其反转排名最高。
127.0.0.1:6379> zrevrank faveGuitarists "Joe Pass"
(integer) 8
成员的分数和排名之间的唯一关系是他们的分数与其他成员的分数相比。如果两个相邻成员之间存在分数差距,这不会反映在他们的排名上。请注意,如果两个成员的分数相同,按字母顺序排列在前的成员将获得较低的排名。
与 zscore
类似,如果键或成员不存在,zrank
和 zrevrank
将返回 (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中管理已排序集合》的第5部分(共6部分)。
Redis提供了两个命令,允许您比较多个有序集合的成员,并基于这些比较创建新的有序集合:zinterstore
和 zunionstore
。为了演示这些命令,请运行以下 zadd
命令来创建一些示例有序集合:
zadd NewKids 1 "Jonathan" 2 "Jordan" 3 "Joey" 4 "Donnie" 5 "Danny"
zadd Nsync 1 "Justin" 2 "Chris" 3 "Joey" 4 "Lance" 5 "JC"
zinterstore
命令用于查找两个或多个有序集合之间的共同成员(即它们的交集),并生成一个新的有序集合,其中仅包含这些共同成员。该命令必须按顺序包含一个目标键的名称(用于存储交集成员作为有序集合)、传递给 zinterstore
的键的数量,以及您想要分析的键的名称。
zinterstore BoyBands 2 NewKids Nsync
zinterstore
返回一个整数,表示存储在目标有序集合中的元素数量。由于 NewKids
和 Nsync
只共享一个成员“Joey”,因此该命令将返回1。
(integer) 1
请注意,如果目标键已经存在,zinterstore
将会覆盖其内容。
zunionstore
会创建一个新的有序集合,其中包含传递给它的每个键的成员。该命令使用与 zinterstore
相同的语法,并需要一个目标键的名称、传递给命令的键的数量和键的名称。
zunionstore SuperGroup 2 NewKids Nsync
和 zinterstore
一样,zunionstore
也会返回一个整数,表示存储在目标键中的元素数量。尽管两个原始有序集合都有五个成员,但由于有序集合不能有重复成员,并且每个键都有一个名为“Joey”的成员,所以最终返回的整数将是9。
(integer) 9
与 zinterstore
类似,如果目标键已经存在,zunionstore
将覆盖其内容。
为了在使用 zinterstore
和 zunionstore
创建新的有序集合时,给予您更多对成员分数的控制权,这两个命令都接受 WEIGHTS
和 AGGREGATE
选项。
WEIGHTS
选项后面跟着一个数字,用于对命令中包含的每个有序集合的成员分数进行加权或乘法处理。WEIGHTS
选项后的第一个数字用于加权第一个传递给命令的键的分数,第二个数字用于加权第二个键,依此类推。
以下示例创建一个新的有序集合,其中包含 NewKids
和 Nsync
有序集合中交集的键。它通过三倍的因子加权 NewKids
键的分数,并通过七倍的因子加权 Nsync
键的分数。
zinterstore BoyBandsWeighted 2 NewKids Nsync WEIGHTS 3 7
如果不包括 WEIGHTS
选项,则 zinterstore
和 zunionstore
的权重默认为1。
聚合函数有三个子选项。其中之一是 SUM
,它通过将合并集合中匹配成员的分数相加来实现 zinterstore
和 zunionstore
的默认行为。
如果你在两个共享一个成员的有序集合上运行 zinterstore
或 zunionstore
操作,但这个成员在每个集合中的分数不同,你可以使用 MIN
子选项强制操作在新集合中分配两个分数中较低的那个分数。
zinterstore BoyBandsWeightedMin 2 NewKids Nsync WEIGHTS 3 7 AGGREGATE MIN
因为这两个有序集合只有一个匹配成员有相同的分数(3),所以这个命令将创建一个新的集合,其中包含具有两个加权分数中较低的那个成员。
zscore BoyBandsWeightedMin "Joey"
这是文章《如何在Redis中管理已排序集合》的第6部分(共6部分)。
同样,AGGREGATE
可以通过 MAX
选项强制 zinterstore
或 zunionstore
将两个分数中较高的一个赋值。
- zinterstore BoyBandsWeightedMax 2 NewKids Nsync WEIGHTS 3 7 AGGREGATE MAX
这个命令会创建一个新的集合,其中只包含一个成员“Joey”,他的权重分数为两个分数中较高的一个。
- zscore BoyBandsWeightedMax “Joey”
21
在分析成员数据之前,将权重视为一种临时操控成员得分的方法是有帮助的。同样地,将聚合选项视为一种控制成员得分如何加入新数据集的方式也是有帮助的。
结论
本指南详细介绍了在Redis中用于创建和管理有序集合的一些命令。如果本指南中还有其他相关命令、参数或程序,您想了解的话,请在评论中提问或提出建议。
有关Redis命令的更多信息,请查看我们关于如何管理Redis数据库的教程系列。