Redis 06_Redis有序集合ZSets

一、Redis中ZSets数据类型简介

1.1 ZSets简介

ZSets(有序集合) 又称Sotred Sets,是 Redis 数据库中的一种数据类型,它和集合(Set)一样也是 String 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数,redis 正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。

ZSets(有序集合) 按照分数以递增的方式进行排序,相同的成员(member)只存在一次,有序集合不允许存在重复的成员,分数可以通过 ZADD 命令进行更新或者也可以通过 ZINCRBY 命令递增来修改之前的值,相应的它们的排序位置也会随着分数变化而改变。获取一个成员当前的分数可以使用 ZSCORE 命令,也可以用它来验证成员是否存在。

ZSets(有序集合)成员的分数可以精确表示的整数的范围 Redis 有序集合的分数使用双精度64位浮点数。支持所有的架构,这表示为一个 IEEE 754 floating point number,它能包括的整数范围是-(2^53)到+(2^53)。或者说是-9007199254740992到 9007199254740992。更大的整数在内部用指数形式表示,所以,如果为分数设置一个非常大的整数,将得到的是一个近似的十进制数。

ZSets(有序集合)相同分数的成员 ZSets(有序集合)里面的成员都是唯一的(不能重复),但是,不同成员间有可能有相同的分数。当多个成员有相同的分数时,它们将是有序的字典(ordered lexicographically)(仍由分数作为第一排序条件,然后,相同分数的成员按照字典规则相对排序)。字典顺序排序用的是二进制,它比较的是字符串的字节数组。如果用户将所有元素设置相同分数(例如0),有序集合里面的所有元素将按照字典顺序进行排序,范围查询元素可以使用 ZRANGEBYLEX 命令(注:范围查询分数可以使用 ZRANGEBYSCORE 命令)。

ZSets(有序集合) 的特点和用途:

  • 元素唯一性:ZSet 中的元素是唯一的,不允许重复元素存在。
  • 有序性:与普通集合不同,ZSet 中的元素是有序排列的,根据元素的分数从小到大排序。
  • 分数与元素关联:每个元素都与一个分数相关联,分数用于确定元素的位置。
  • 高效的成员查找:Redis 提供了高效的成员查找操作,可以根据成员名字快速查找分数。
  • 范围查询:ZSet 支持根据分数范围进行查询,例如获取分数在某个范围内的元素。
  • 排行榜:ZSet 常用于构建排行榜,其中分数可以表示用户的得分或者其它权重值。

1.2 ZSets 类型内部编码及底层结构介绍

ZSets(有序集合) 的编码可以是压缩列表(ziplist)或者跳跃表(skiplist)。

压缩列表(ziplist) ziplist 编码的 ZSet列表对象使用压缩列表作为底层实现,压缩列表以连续的内存块形式存储数据,每个节点可以包含一个或多个元素,而且它可以非常紧凑地存储整数和字符串等不同类型的元素。每个 ZSet 集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员(member),而第二个元素则保存元素的分值(score)。

压缩列表内的集合元素按分值从小到大进行排序,分值较小的元素被放置在靠近表头的方向,而分值较大的元素则被放置在靠近表尾的方向。 需要注意的是,当有序集合对象可以同时满足以下两个条件时,对象才使用 ziplist 编码:

  • 当有序集合保存的元素数量小于 zset-max-ziplist-entries 配置的值,默认是 128;
  • 当有序集合保存的所有元素成员(member)的长度都小于 zset-max-ziplist-value 配置的值,默认是 64 字节。

如果不能满足以上 2 个条件的有序集合对象将使用跳跃表(skiplist)编码。

跳跃表(skiplist) 跳跃表(skiplist)是一种基于链表的数据结构,通常用于存储元素较多、元素较大的有序集合。跳跃表使用多级索引来加速元素的查找操作,每级索引包含一部分元素,从而减少了查找元素的时间复杂度。

skiplist 编码的有序集合对象使用 zset 结构作为底层实现,一个 zset 结构同时包含一个字典和一个跳跃表:

1
2
3
4
typedef struct zset {
    zskiplist *zsl; //  跳跃表
    dict *dict;     //  字典
} zset;

zset 结构中的 zsl 跳跃表按分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素:跳跃表节点的 object 属性保存了元素的成员,而跳跃表节点的 score 属性则保存了元素的分值。通过这个跳跃表,程序可以对有序集合进行范围型操作,比如 ZRANGEZRANK 等命令就是基于跳跃表 API 来实现的。

此外,zset 结构中的 dict 字典为有序集合创建了一个从成员到分值的映射,字典中的每个键值对都保存了一个集合元素:字典的键保存了元素的成员,而字典的值则保存了元素的分值。通过这个字典,程序可以用 O(1) 复杂度查找指定成员的分值,ZSCORE 命令就是根据这一特性实现的,而很多其它有序集合命令都在实现的内部用到了这一特性。

值得一提的是,虽然 zset 结构同时使用跳跃表和字典来保存有序集合元素,但这两种数据结构都会通过指针来共享相同元素的成员和分值,所以同时使用跳跃表和字典来保存集合元素不会产生任何重复成员或者分值,也不会因此而浪费额外的内存。

跳跃表的具体存储形式,如下图:

二、ZSets类型相关命令

2.1 Redis ZSets 相关命令简介

ZSet(有序集合)是 Redis 中的一种数据类型,它类似于 Set,但每个元素都会关联一个分数(score),使得集合中的元素能够按照分数进行排序。以下是关于 Redis ZSet 相关命令的简介表,包括命令、作用以及时间复杂度:

命令 作用 时间复杂度
ZADD 添加或更新有序集合中的元素和分数 O(log(N)*M) (N 为有序集合的大小,M 为要添加/更新的元素数量)
ZCARD 获取有序集合中元素的数量 O(1)
ZCOUNT 计算有序集合中分数范围内的元素数量 O(log(N))
ZRANGE 获取有序集合中指定索引范围的元素(升序) O(log(N)+M) (N 为有序集合的大小,M 为返回的元素数量)
ZRANGEBYLEX 返回指定成员区间内的成员,按成员字典正序排序,且成员的分数必须相同。 O(log(N)+M),N 为 有序集合的基数(成员个数),M 为指定的返回条数
ZRANGEBYSCORE 获取有序集合中符合分数范围条件的元素 O(log(N)+M) (N 为有序集合的大小,M 为返回的元素数量)
ZREVRANGE 获取有序集合中指定索引范围的元素(降序) O(log(N)+M) (N 为有序集合的大小,M 为返回的元素数量)
ZREVRANGEBYLEX 返回指定成员区间内的成员,成员按字典倒序排序。 O(log(N)+M),N 为 有序集合的基数(成员个数),M 为结果集的基数
ZREVRANGEBYSCORE 返回有序集合 key 中,所有 score 值介于 max 和 min 之间的成员。有序集合成员按 score 值递减(从大到小)次序排列。 O(log(N)+M),N 为有序集合的基数(成员个数),M 为指定的返回条数
ZPOPMAX 弹出并返回有序集合中分数最高的元素 O(log(N))
ZPOPMIN 弹出并返回有序集合中分数最低的元素 O(log(N))
ZRANK 获取指定元素在有序集合中的排名(升序) O(log(N))
ZREVRANK 获取指定元素在有序集合中的排名(降序) O(log(N))
ZSCORE 获取指定元素在有序集合中的分数 O(1)
ZINCRBY 对指定元素的分数进行增减操作 O(log(N))
ZREM 从有序集合中移除指定的元素 O(log(N)*M) (N 为有序集合的大小,M 为要移除的元素数量)
ZREMRANGEBYLEX 删除名称按字典由低到高排序的成员之间所有成员 O(log(N)+M),N 为有序集的基数,M 为被成功移除的成员的数量
ZREMRANGEBYRANK 根据排名范围移除有序集合中的元素 O(log(N)+M) (N 为有序集合的大小,M 为要移除的元素数量)
ZREMRANGEBYSCORE 根据分数范围移除有序集合中的元素 O(log(N)+M) (N 为有序集合的大小,M 为要移除的元素数量)
ZINTERSTORE 计算多个有序集合的交集并存储结果 O(NK)+O(Mlog(M)) (N 为输入有序集合的数量,K 为最小有序集合的大小,M 为输出有序集合的大小)
ZUNIONSTORE 计算多个有序集合的并集并存储结果 O(NK)+O(Mlog(M)) (N 为输入有序集合的数量,K 为最小有序集合的大小,M 为输出有序集合的大小)

ZSet(有序集合)是 Redis 中强大且灵活的数据类型,常用于需要元素排序和按分数检索的场景,如排行榜、带权重的任务队列等。通过合理选择 ZSet 命令,可以高效地处理这些应用场景。

2.2 ZADD 命令

ZADD 命令将一个或多个 member 元素及其 score 值加入到有序集 key 当中。分数必须是 double 类型,可以是正数、负数、正负无穷大等。ZADD 命令的相关选项包括:

如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值,并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。score 值是一个双精度的浮点型数字字符串。如果 key 不存在,则创建一个空的有序集并执行 ZADD 操作。当 key 存在但不是有序集类型时,返回一个错误。

CH:默认情况下,ZADD 返回添加的元素数量,但指定这个选项后,它会返回添加和更新的元素数量。 INCR:此选项类似于 ZINCRBY 命令,用于对指定元素的分数进行增量操作。

命令语法:

1
127.0.0.1:6379> ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]

命令参数: ZADD 命令在 key 后面分数/成员(score/member)对前面支持一些参数,它们都是 3.0.2 版本及之后引入的。

  • NX:仅用于添加新元素,不会更新已经存在的元素;
  • XX:仅用于更新已经存在的元素,不会添加新元素;
  • CH:设置返回值为发生变化的成员总数,默认情况下,ZADD 返回添加的元素数量,但指定这个选项后,它会返回添加和更新的元素数量;
  • INCR:此选项类似于 ZINCRBY 命令,用于对指定元素的分数进行增量操作,该参数只支持每次操作一对分数/成员(score/member);

命令返回值:

  • 当指定的 key 不存在时创建有序集合key,并将 score-member对 添加到有序集合key中,返回添加的成员数量;
  • 当指定的 key 存在 且是有序集合key 时,将 score-member对 添加到有序集合key中,返回添加的成员数量,不包括已经存在更新分数的成员;
  • 当指定的 key 不是ZSet集合时,返回类型不匹配的错误信息;

版本历史: <2.4,只能添加或者更新一个成员;

=2.4,可接受多个成员; =3.0.2,增加了 [NX|XX] [CH] [INCR] 参数。

2.3 ZCARD 和 ZCOUNT 命令

1、ZCARD 命令 ZCARD 命令返回有序集合 key 的基数(即成员的个数),也可称之为成员的数目。可以用 ZCARD 命令来先查看那些大 key 的有序集合,以便根据实际大小,制定相应的下一步操作策略,避免不必要的大 key 查询。

命令语法:

1
127.0.0.1:6379> ZCARD key

命令返回值:

  • 当 key 存在且是有序集合类型时,返回有序集合的基数(即成员的个数);
  • 当 key 不存在时,返回 0;
  • 当 key 不是ZSet集合时,返回类型不匹配的错误信息;

2、 ZCOUNT 命令 ZCOUNT 命令返回有序集合 key 中,score 值在 min 和 max 之间(score 值包含 min 和 max 值)的成员的数量。 命令语法:

1
127.0.0.1:6379> ZCOUNT key min max

命令返回值:

  • 当 key 存在且是有序集合类型时,返回有序集合key 成员的score 值在 min 和 max 之间(score 值包含 min 和 max 值)的成员的数量;
  • 当 key 不存在时,返回 0;
  • 当 key 不是ZSet集合时,返回类型不匹配的错误信息;

2.4 ZRANGE、ZRANGEBYLEX 和 ZRANGEBYSCORE 命令

1、 ZRANGE 命令 ZRANGE 命令返回有序集合 key 中,指定区间内的成员,成员的位置按 score 值递增(从小到大)来排序,具有相同 score 值的成员按字典序来排列(如果你需要成员按 score 值递减来排列,请使用 ZREVRANGE 命令)。 命令语法:

1
127.0.0.1:6379> ZRANGE key start stop [WITHSCORES]

命令参数:

  • 下标参数 startstop 一般都以0为底,也就是说,以0表示有序集第一个成员,以1表示有序集第二个成员,以此类推;也可以使用负数下标,以-1表示最后一个成员,-2表示倒数第二个成员,以此类推。超出范围的下标并不会引起错误。比如说,当 start 的值比有序集的最大下标还要大,或是 start > stop 时,ZRANGE 命令只是简单地返回一个空列表。另一方面,假如 stop 参数的值比有序集的最大下标还要大,那么 Redis 将 stop 当作最大下标来处理。
  • WITHSCORES 选项用来让成员和它的 score 值一并返回,返回列表以 value1,score1, ..., valueN,scoreN 的格式表示。

命令返回值:

  • 如果 key 不存在,返回 (empty array);
  • 当 key 存在且是有序集合类型时, 返回 指定 [start, stop] 区间内,的有序集成员(可选 带有 score 值)的列表;
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

2、ZRANGEBYLEX 命令 ZRANGEBYLEX 的作用是当有序集合 key 的所有成员都具有相同的分值时(有序集合的元素会根据成员的字典序[lexicographical ordering] 来进行排序),返回给定的有序集合 key 中值介于 minmax 之间的成员。如果有序集合里面的成员带有不同的分值,那么命令返回的结果是未指定的(unspecified)

默认是以 ASCII 字符集的顺序进行排列。如果成员字符串包含 utf-8 这类字符集的内容,就会影响返回结果,所以建议不要使用。

源码中采用 C 语言中 memcmp() 函数,从字符的第0位到最后一位进行排序,如果前面部分相同,那么较长的字符串比较短的字符串排序靠后。

命令语法:

1
127.0.0.1:6379> ZRANGEBYLEX key min max [LIMIT offset count]

参数说明:

  • min:字典中排序位置较小的成员,必须以 ( 开头,或者以 [ 开头,可使用 - 代替;
  • max:字典中排序位置较大的成员,必须以 ( 开头,或者以 [ 开头,可使用 + 代替;

Tips:

  1. 其中 ( 表示开区间,指定的值不会被包含在范围之内;而 [ 则表示闭区间,指定的值会被包含在范围之内。
  2. +- 在 min 参数以及 max 参数中具有特殊的意义,其中 + 表示正无限, 而 - 表示负无限。因此,向一个所有成员的分值都相同的有序集合发送命令 ZRANGEBYLEX <zset> - + 将返回有序集合中的所有元素。
  • LIMIT:返回结果是否分页,指令中包含 LIMIT 后 offset、count 必须输入;
  • offset:返回结果起始位置;
  • count:返回结果数量;

命令返回值:

  • 如果 key 集合不存在,仅返回 (empty array);
  • 如果 key 存在 并且 是有序集合,如果集合 key 中 min、max 指定范围内的元素分值相同时,返回指定范围内的元素列表,区间内分值不同时,返回的结构是不确定的;
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

3、 ZRANGEBYSCORE 命令 ZRANGEBYSCORE 命令返回有序集合的指定 key 中,所有 score 值介于 min 和 max 之间(包含等于 min 和 max)的成员(默认情况下),也可以通过给 min 和 max 对应参数前增加 ( 符号来使用可选的开区间(小于或大于)。

有序集合成员按 score 值递增(从小到大)顺序排列;此外,具有相同 score 值的成员按字典序(lexicographical order)来排列(该属性是有序集提供的,不需要额外的计算)。

命令语法:

1
127.0.0.1:6379> ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

参数说明:

  • min 参数必需,最小的 score(分数)值,参数前增加 ( 符号来 表示大于 min,min 可以使用 -inf 代替表示无限小;
  • max 参数必需,最大的 score(分数)值,参数前增加 ( 符号来 表示小于 max,max 可以使用 +inf 代替表示无限大;

Tips: min 和 max 可以是 -inf 和 +inf,这样一来,你就可以在不知道有序集的最低和最高 score 值的情况下,使用 ZRANGEBYSCORE 这类命令。

  • WITHSCORES 参数可选,该参数将有序集成员及其 score 值一起返回,该选项自 Redis 2.0 版本起可用;
  • LIMIT offset count 参数可选,取值的范围,offset、表示结果的起始位置(或偏移位置),count、表示返回结果的数量。值得注意的是,当 offset 很大时,定位 offset 的操作可能需要遍历整个有序集,此过程最坏复杂度为 O(N)。

命令返回值:

  • 如果 key 不存在,返回 (empty array);
  • 如果 key 存在 并且 是有序集合,返回 所有 score 值介于 min,max 区间的元素列表(带有 score 值 是可选项);
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

2.5 ZREVRANGE、ZREVRANGEBYLEX 和 ZREVRANGEBYSCORE 命令

1、 ZREVRANGE 命令 ZREVRANGE 命令返回有序集 key 中,指定区间内的成员。其中成员的位置按 score 值递减(从大到小)来排列。具有相同 score 值的成员按字典序的反序排列。除了成员按 score 值递减的次序排列这一点外,ZREVRANGE 命令的其它方面和 ZRANGE 命令一样。 命令语法:

1
127.0.0.1:6379> ZREVRANGE key start stop [WITHSCORES]

命令返回值:

  • 如果 key 不存在,返回 (empty array);
  • 当 key 存在且是有序集合类型时, 返回 指定 [start, stop] 区间内,的有序集成员(可选 带有 score 值)的列表;
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

2、ZREVRANGEBYLEX 命令 ZREVRANGEBYLEX 命令返回指定成员区间内的成员,成员按字典倒序排序,且分数必须相同。除了成员值递减的次序排列这一点外,ZREVRANGE 命令的其它方面和 ZRANGEBYLEX 命令一样。 命令语法:

1
127.0.0.1:6379> ZREVRANGEBYLEX key max min [LIMIT offset count]

命令返回值:

  • 如果 key 集合不存在,仅返回 (empty array);
  • 如果 key 存在 并且 是有序集合,如果集合 key 中 min、max 指定范围内的元素分值相同时,返回指定范围内的元素列表,区间内分值不同时,返回的结构是不确定的;
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

3、ZREVRANGEBYSCORE 命令 ZREVRANGEBYSCORE 命令返回有序集合 key 中,所有 score 值介于 max 和 min 之间(包括等于 min 和 max)的成员。有序集合成员按 score 值递减(从大到小)次序排列。除了成员值递减的次序排列这一点外,ZREVRANGEBYSCORE 命令的其它方面和 ZRANGEBYSCORE 命令一样。 命令语法:

1
127.0.0.1:6379> ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

命令返回值:

  • 如果 key 不存在,返回 (empty array);
  • 如果 key 存在 并且 是有序集合,返回 所有 score 值介于 min,max 区间的元素列表(带有 score 值 是可选项);
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

2.6 ZPOPMAX 和 ZPOPMIN、BZPOPMAX 和 BZPOPMIN 命令

1、ZPOPMAX 命令 ZPOPMAX 命令删除并返回有序集合 key 中的最多 count 个具有最高得分的成员和得分列表(top count,count >= 1)。当返回多个元素时候,得分最高的元素将是第一个元素,然后是分数较低的元素。

命令语法:

1
127.0.0.1:6379> ZPOPMAX key [count]

命令参数:

  • 如未指定,count 的默认值为 1;
  • 指定一个大于有序集合的基数(成员的个数)的 count 不会产生错误;

命令返回值:

  • 如果 key 列表全不存在,则返回 (empty array);
  • 如果 key 列表存在 并且 全是有序集合,弹出(删除并返回)集合的得分最高的 top count 个元素的值 和得分的列表;
  • 当指定的 key 列表有至少一个不是集合时,返回类型不匹配的错误信息;

2、ZPOPMIN 命令 ZPOPMIN 命令删除并返回有序集合 key 中的最多 count 个具有最低得分的成员和得分列表(low count,count >= 1)。当返回多个元素时候,得分最低的元素将是第一个元素,然后是分数较高的元素。

命令语法:

1
127.0.0.1:6379> ZPOPMIN key [count]

命令参数:

  • 如未指定,count 的默认值为 1;
  • 指定一个大于有序集合的基数(成员的个数)的 count 不会产生错误;

命令返回值:

  • 如果 key 列表全不存在,则返回 (empty array);
  • 如果 key 列表存在 并且 全是有序集合,弹出(删除并返回)集合的得分最低的 low count 个元素的值 和 得分的列表;
  • 当指定的 key 列表有至少一个不是集合时,返回类型不匹配的错误信息;

3、BZPOPMAX 命令 BZPOPMAX 命令是有序集合命令 ZPOPMAX 带有阻塞功能的版本。在参数中的所有有序集合均为空的情况下,阻塞连接。参数中包含多个有序集合时,按照参数中 key 的顺序,返回第一个非空 key 中分数最大的成员和对应的分数,参数 timeout 可以理解为客户端被阻塞的最大秒数值,0表示永久阻塞。 命令语法:

1
127.0.0.1:6379> BZPOPMAX key [key ...] timeout

命令返回值:

  • 当有序集合 key 无结果返回或者超时设置过期时返回 nil;
  • 当指定的 key 列表 第一个非空 key 是有序集合时,弹出(删除并返回)第一个非空序集合 key、key 中分数最大的成员 和 对应的分数 组成的三元列表。
  • 当指定的 key 列表 第一个非空 key 不是有序集合时,返回类型不匹配的错误信息;

4、BZPOPMIN 命令 BZPOPMIN 命令是有序集合命令 ZPOPMIN 带有阻塞功能的版本。在参数中的所有有序集合均为空的情况下,阻塞连接。参数中包含多个有序集合时,按照参数中 key 的顺序,返回第一个非空 key 中分数最大的成员和对应的分数,参数 timeout 可以理解为客户端被阻塞的最大秒数值,0表示永久阻塞。 命令语法:

1
127.0.0.1:6379> BZPOPMIN key [key ...] timeout

命令返回值:

  • 当有序集合 key 无结果返回或者超时设置过期时返回 nil;
  • 当指定的 key 列表 第一个非空 key 是有序集合时,弹出(删除并返回)第一个非空序集合 key、key 中分数最小的成员 和 对应的分数 组成的三元列表。
  • 当指定的 key 列表 第一个非空 key 不是有序集合时,返回类型不匹配的错误信息;

2.7 ZRANK、ZREVRANK、ZSCORE 和 ZINCRBY 命令

1、ZRANK 命令 ZRANK 命令返回有序集 key 中成员 member 的排名名次。其中有序集成员按 score 值递增(从小到大)顺序排列。排名以 0 为底,也就是说,score 值最小的成员排名为 0。 命令语法:

1
127.0.0.1:6379> ZRANK key member

命令返回值:

  • 如果 key 不存在 或者 key 是有序集合 但 key 集合中没有 member 成员,则返回 (nil);
  • 如果 key 存在并且是有序集合,并且key 集合中有 member 成员,则返 member 成员的排名名次;
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

2、 ZREVRANK 命令 ZREVRANK 命令返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)顺序排列。排名以0为底,也就是说,score 值最大的成员排名为0。 命令语法:

1
127.0.0.1:6379> ZREVRANK key member

命令返回值:

  • 如果 key 不存在 或者 key 是有序集合 但 key 集合中没有 member 成员,则返回 (nil);
  • 如果 key 存在并且是有序集合,并且key 集合中有 member 成员,则返 member 成员的不排名名次;
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

3、ZSCORE 命令 ZSCORE 命令返回有序集 key 中,成员 member 的 score 值。 命令语法:

1
127.0.0.1:6379> ZSCORE key member

命令返回值:

  • 如果 key 不存在 或者 key 是有序集合 但 key 集合中没有 member 成员,则返回 (nil);
  • 如果 key 存在并且是有序集合,并且key 集合中有 member 成员,则返 member 成员的 score 值(double 型浮点数),以字符串形式表示;
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

4、ZINCRBY 命令 ZINCRBY 命令为有序集合 key 的成员 member 的 score 值加上增量 increment。

当 key 不存在,或 member 不是 key 的成员时,ZINCRBY key increment member 等同于 ZADD key increment member

命令语法:

1
127.0.0.1:6379> ZINCRBY key increment member

命令参数:

  • 可以通过传递一个负数值 increment,让 score 减去相应的值,比如 ZINCRBY key -5 member,就是让 member 的 score 值减去 5;

命令返回值:

  • 如果 key 不存在 或者 key 是有序集合 但 key 集合中没有 member 成员,创建 有序集 key 或 将 member 成员及其 score 值 increment 添加到 有序集 key 中并 返回 元素的 score 值increment;
  • 如果 key 存在并且是有序集合,并且key 集合中有 member 成员,则将 member 成员的 score 值加 increment 后返回 member 成员的 新score 值;
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

2.8 ZREM、ZREMRANGEBYLEX、ZREMRANGEBYRANK 和 ZREMRANGEBYSCORE 命令

1、ZREM 命令 ZREM 命令移除有序集 key 中的一个或多个成员,并返回移除的成员列表,不存在的成员将被忽略。在 Redis 2.4 版本以前,ZREM 每次只能删除一个元素。 命令语法:

1
127.0.0.1:6379> ZREM key member [member ...]

命令返回值:

  • 如果 key 不存在 或者 key 是有序集合 但 key 集合中没有 member 成员,则返回 0;
  • 如果 key 存在并且是有序集合,并且 key 集合中有至少其中一个 member 成员,移除 有序集合key 中存在的member 成员 并返回移除的 member 成员数量;
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

2、ZREMRANGEBYLEX 命令 命令 ZREMRANGEBYLEX 的作用是当有序集合 key 的所有成员都具有相同的分值时,删除名称按字典由低到高排序的成员之间所有成员(有序集合的元素会根据成员的字典序[lexicographical ordering] 来进行排序),返回返回删除的元素成员数。如果有序集合里面的成员带有不同的分值,那么命令返回的结果是未指定的(unspecified)

默认是以 ASCII 字符集的顺序进行排列。如果成员字符串包含 utf-8 这类字符集的内容,就会影响删除成员及返回结果,所以建议不要使用。

源码中采用 C 语言中 memcmp() 函数,从字符的第0位到最后一位进行排序,如果前面部分相同,那么较长的字符串比较短的字符串排序靠后。

不要在成员分数不同的有序集合中使用此命令,因为它是基于分数一致的有序集合设计的,如果使用,会导致删除的结果不正确。待删除的有序集合中,分数最好相同,否则删除结果会不正常。 命令语法:

1
127.0.0.1:6379> ZREMRANGEBYLEX key min max

参数说明:

  • min:字典中排序位置较小的成员,必须以 ( 开头,或者以 [ 开头,可使用 - 代替;
  • max:字典中排序位置较大的成员,必须以 ( 开头,或者以 [ 开头,可使用 + 代替;

Tips:

  1. 其中 ( 表示开区间,指定的值不会被包含在范围之内;而 [ 则表示闭区间,指定的值会被包含在范围之内。
  2. +- 在 min 参数以及 max 参数中具有特殊的意义,其中 + 表示正无限, 而 - 表示负无限。因此,向一个所有成员的分值都相同的有序集合发送命令 ZREMRANGEBYLEX key - + 将删除有序集合中的所有元素。

命令返回值:

  • 如果 key 不存在,仅返回 0;
  • 如果 key 存在 并且 是有序集合,如果集合 key 中 min、max 指定范围内的元素分值相同时,删除指定范围内的元素列表,区间内分值不同时,删除的结过是不确定的, 返回删除的元素数量;
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

3、ZREMRANGEBYRANK 命令 ZREMRANGEBYRANK 命令移除有序集 key 中,指定排名区间内的所有成员。 命令语法:

1
127.0.0.1:6379> ZREMRANGEBYRANK key start stop

参数说明:

  • 下标参数 start 和 stop 都以0为底,0处是分数最小的那个元素。这些索引也可以是负数,表示位移从最高分处开始数。例如,-1是分数最高的元素,-2是分数第二高的,依次类推。

命令返回值:

  • 如果 key 不存在,仅返回 0;
  • 如果 key 存在 并且 是有序集合,删除 有序集合 key 中排名区在 start 到 stop之间的成员,返回删除的元素数量;
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

4、ZREMRANGEBYSCORE 命令 ZREMRANGEBYSCORE 命令移除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max)的成员。该命令语法与 ZRANGEBYSCORE 命令。 自版本 2.1.6 开始,score 值等于 min 或 max 的成员也可以不包括在内。 命令语法:

1
127.0.0.1:6379> ZREMRANGEBYSCORE key min max

命令返回值:

  • 如果 key 不存在,仅返回 0;
  • 如果 key 存在 并且 是有序集合,删除 有序集合 key 中 score值 在 min 到 max之间的成员,返回删除的元素数量;
  • 当指定的 key 不是有序集合时,返回类型不匹配的错误信息;

2.9 ZINTERSTORE 和 ZUNIONSTORE 命令

1、ZINTERSTORE 命令 ZINTERSTORE 命令计算给定的一个或多个有序集的交集,其中给定 key 的数量必须以 numkeys 参数指定,并将该交集(结果集)储存到 destination。

1
127.0.0.1:6379> ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

命令参数:

  • 默认情况下,WEIGHTS 是 1,AGGREGATE 是 SUM,即结果集中某个成员的分数值是所有给定集下该成员分数值之和;

命令返回值:

  • 保存到 destination 的结果集的基数(成员的个数)。

2、ZUNIONSTORE 命令 ZUNIONSTORE 命令计算给定的 numkeys 个有序集合的并集,并且把结果放到 destination 中。在给定要计算的 key 和其它参数之前,必须先给定 key 个数(numberkeys)。 默认情况下,结果集中某个成员的 score 值是所有给定集下该成员 score 值之和。如果 key destination 存在,就被覆盖。

1
127.0.0.1:6379> ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

命令参数:

  • 使用 WEIGHTS 选项,你可以为每个给定的有序集指定一个乘法因子,意思就是,每个给定有序集的所有成员的 score 值在传递给聚合函数之前都要先乘以该因子。如果 WEIGHTS 没有给定,默认就是1。

  • 使用 AGGREGATE 选项,你可以指定并集的结果集的聚合方式。默认使用的参数 SUM,可以将所有集合中某个成员的 score 值之和作为结果集中该成员的 score 值。如果使用参数 MIN 或者 MAX,结果集就是所有集合中元素最小或最大的元素。

命令返回值 结果有序集合 destination 中元素个数。

2.9 ZSCAN 和 ZLEXCOUNT 命令

1、ZSCAN 命令 ZSCAN 命令用于迭代有序集合中的元素(包括元素成员和元素分值)。

1
127.0.0.1:6379> ZSCAN key cursor [MATCH pattern] [COUNT count]

参数说明:

  • cursor 参数:ZSCAN 命令每次被调用之后,都会向用户返回一个新的游标,用户在下次迭代时需要使用这个新游标作为 ZSCAN 命令的游标参数,以此来延续之前的迭代过程。当 ZSCAN 命令的游标参数被设置为0时,服务器将开始一次新的迭代,而当服务器向用户返回值为0的游标时,表示迭代已结束。
  • match 参数:和 KEYS 命令一样,增量式迭代命令也可以通过提供一个 glob 风格的模式参数,让命令只返回和给定模式相匹配的元素,这一点可以通过在执行增量式迭代命令时,通过给定 MATCH 参数来实现。
  • count 参数:虽然增量式迭代命令不保证每次迭代所返回的元素数量,但我们可以使用 COUNT 选项,对命令的行为进行一定程度上的调整。基本上,COUNT 选项的作用就是让用户告知迭代命令,在每次迭代中应该从数据集里返回多少元素。count 参数的默认值为 10。

命令返回值:

  • 返回的每个元素都是一个有序集合元素,一个有序集合元素由一个成员(member)和一个分值(score)组成。

2、ZLEXCOUNT 命令 ZLEXCOUNT 命令计算有序集合中指定字典区间内成员数量。对于一个所有成员的分值都相同的有序集合键 key 来说,这个命令会返回该集合中,成员介于 min 和 max 范围内的元素数量。

1
127.0.0.1:6379> ZLEXCOUNT key min max

命令返回值:

  • 返回指定范围内的成员数量。

三、ZSet 使用场景

3.1 排行榜系统

假设开发一个社交平台,需要维护每天的用户赞数排行榜。以下是如何使用 Redis 的有序集合来实现这个场景: 1、添加用户赞数 当用户发布一篇文章并获得点赞时,使用 ZADD 命令将用户的点赞数添加到相应的排行榜中,同时使用 ZINCRBY 命令来增加用户的点赞数。

1
2
ZADD user:ranking:2022-03-15 3 james
ZINCRBY user:ranking:2022-03-15 1 james

这些命令将用户 “james” 的点赞数添加到日期为 “2022-03-15” 的排行榜中,并在之后每次获得点赞时增加点赞数。

2、取消用户赞数 如果需要删除用户的点赞数,例如用户注销或发生违规行为,可以使用 ZREM 命令从排行榜中删除用户。

1
ZREM user:ranking:2022-03-15 tom

这个命令将用户 tom 从排行榜中删除。

3、展示获取赞数最多的用户 要展示获取赞数最多的用户,可以使用 ZREVRANGE 命令,它返回指定排行榜的前 N 名用户。

1
ZREVRANGE user:ranking:2022-03-15 0 9

这个命令将返回日期为 2022-03-15 的排行榜中前 10 名用户。

4、展示用户信息和分数 用户信息可以存储在哈希类型中,每个用户的信息使用用户名称作为键后缀。使用 HGETALL 命令可以获取用户的详细信息,使用 ZSCORE 和 ZRANK 命令可以获取用户的分数和排名。

1
2
3
HGETALL user:info:james
ZSCORE user:ranking:2022-03-15 mike
ZRANK user:ranking:2022-03-15 mike

通过这些 Redis 操作,可以实现一个完整的排行榜系统,用于跟踪用户的点赞数、取消点赞数、展示排行榜和获取用户信息。这对于社交平台、新闻网站等应用非常有帮助,可以提供实时的排名和用户信息。

3.2 需要随机获取数据源中的元素根据某个权重进行排序的场景

举例:各种排行榜比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。 相关命令:ZRANGE (从小到大排序)、 ZREVRANGE (从大到小排序)、ZREVRANK (指定元素排名)。

3.3 需要存储的数据有优先级或者重要程度的场景 比如优先级任务队列。

举例:优先级任务队列。 相关命令:ZRANGE (从小到大排序)、 ZREVRANGE (从大到小排序)、ZREVRANK (指定元素排名)。