精华内容
下载资源
问答
  • 面试官:Redis中有序集合的内部实现方式是什么?
    2022-03-10 10:13:06

    面试官:Redis中基本的数据类型有哪些?

    我:Redis的基本数据类型有:字符串(string)、哈希(hash)、列表(list)、集合(set)、有序集合(zset)。

    面试官:有序集合的内部实现方式是什么?

    我还沉浸在上一个问题的沾沾自喜中,顿时表情凝固了,手心开始冒出冷汗。“这个。。没有太深入了解”,我支支吾吾的说到。

    面试官:回去等消息吧。

    这句话说的干净利落,然后就没有然后了。失败是成功的妈妈,我不气馁,决定马上恶补一下。

    有序集合的内部实现

    有序集合的内部实现有两种,分别是:压缩列表(ziplist)和跳跃表(skiplist)。接下来,我们分别进行详细的了解。

    以压缩列表作为内部实现

    当有序集合的元素个数小于zset-max-ziplist-entries(默认为128个),并且每个元素成员的长度小于zset-max-ziplist-value(默认为64字节)的时候,使用压缩列表作为有序集合的内部实现。

    每个集合元素由两个紧挨在一起的两个压缩列表结点组成,其中第一个结点保存元素的成员,第二个结点保存元素的分支。压缩列表中的元素按照分数从小到大依次紧挨着排列,有效减少了内存空间的使用。

    举个例子,我们使用zadd命令创建一个以压缩列表为实现的有序集合:

    127.0.0.1:6379> zadd one-more-zset 1 one 2 two 3 three
    (integer) 3
    127.0.0.1:6379> zrange one-more-zset 0 -1
    1) "one"
    2) "two"
    3) "three"
    127.0.0.1:6379> object encoding one-more-zset
    "ziplist"

    以跳跃表作为内部实现

    当有序集合的元素个数大于等于zset-max-ziplist-entries(默认为128个),或者每个元素成员的长度大于等于zset-max-ziplist-value(默认为64字节)的时候,使用跳跃表作为有序集合的内部实现。

    此时,在有序集合中其实包含了两个结构,一个是跳跃表,另一个是哈希表。

    在跳跃表中,所有元素按照从小到大的顺序排列。跳跃表的结点中的object指针指向元素成员的字符串对象,score保存了元素的分数。通过跳跃表,Redis可以快速地对有序集合进行分数范围、排名等操作。

    在哈希表中,为有序集合创建了一个从元素成员到元素分数的映射。键值对中的键指向元素成员的字符串对象,键值对中的值保存了元素的分数。通过哈希表,Redis可以快速查找指定元素的分数。

    虽然有序集合同时使用跳跃表和哈希表,但是这两种数据结构都使用指针共享元素中的成员和分数,不会额外的内存浪费。

    举个例子,我们使用zadd命令创建一个以跳跃表为实现的有序集合:

    127.0.0.1:6379> zadd one-more-zset 1 long-long-long-long-long-long-long-long-long-long-long-long-long-long
    (integer) 1
    127.0.0.1:6379> zrange one-more-zset 0 -1
    1) "long-long-long-long-long-long-long-long-long-long-long-long-long-long"
    127.0.0.1:6379> object encoding one-more-zset
    "skiplist"

    内部实现的转换

    当一个有序集合是以压缩列表作为内部实现时,再向这个有序集合添加较长的元素成员,或向这个有序集合的元素个数过多时,那么这个有序集合就会转换为以跳跃表作为内部实现。但是,以跳跃表作为内部实现的有序集合不会转换为以压缩列表作为内部实现。

    举个例子,我们先创建一个以压缩列表作为内部实现的有序集合:

    127.0.0.1:6379> zadd one-more-zset 1 one 2 two 3 three
    (integer) 3
    127.0.0.1:6379> zrange one-more-zset 0 -1
    1) "one"
    2) "two"
    3) "three"
    127.0.0.1:6379> object encoding one-more-zset
    "ziplist"

    然后,再向它添加一个较长成员的元素,它就是转换为以跳跃表作为内部实现:

    127.0.0.1:6379> zadd one-more-zset 4 long-long-long-long-long-long-long-long-long-long-long-long-long-long
    (integer) 1
    127.0.0.1:6379> zrange one-more-zset 0 -1
    1) "one"
    2) "two"
    3) "three"
    4) "long-long-long-long-long-long-long-long-long-long-long-long-long-long"
    127.0.0.1:6379> object encoding one-more-zset
    "skiplist"

    然后,再把那一个较长成员的元素从有序集合中移除,有序集合依然是以跳跃表作为内部实现:

    127.0.0.1:6379> zrem one-more-zset long-long-long-long-long-long-long-long-long-long-long-long-long-long
    (integer) 1
    127.0.0.1:6379> zrange one-more-zset 0 -1
    1) "one"
    2) "two"
    3) "three"
    127.0.0.1:6379> object encoding one-more-zset
    "skiplist"

    总结

    在Redis中,有序集合的内部实现有压缩列表(ziplist)和跳跃表(skiplist)两种,当集合中的所有元素的成员长度较短并元素个数较少时,使用压缩列表作为内部实现,否则使用跳跃表和哈希表作为内部实现。当条件不满足时,压缩列表可以转换为跳跃表,但跳跃表不能转换为压缩列表。


    竟然已经看到这里了,你我定是有缘人,留下你的点赞关注,他日必成大器。

    更多相关内容
  • 主要给大家介绍了关于如何利用Redis的有序集合实现排行榜功能的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者使用Redis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
  • std::set作为标准库的一个关联容器,实现内部元素进行了排序,使用这特性可以对一组元素进行插入排序。std::set最初的设计是完成数学中“集合”的概念,它提供的接口也是如此。
  • 主要介绍了python redis连接 有序集合去重的代码,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
  • 今天我们说一下Redis中最后一个数据类型 “有序集合类型”,回首之前学过的几个数据结构,不知道你会不会由衷感叹,开源的世界真好,写这些代码的好心人真的要一生平安哈,不管我们想没想的到的东西,在这个世界上都...
  • 主要介绍了使用Redis有序集合实现IP归属地查询,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 一、有序集合类型 有序集合类型,大家从名字上应该就可以知道,实际上就是在集合类型上加了个有序而已。Redis中的有序集合类型,实际上是在集合类型上,为每个元素都关联一个分数,有序实际上说的是分数有序,我们...
  • Redis有序集合(sorted set)使用

    千次阅读 2021-02-12 18:37:05
    有序集合(sorted set)同时拥有“有序” 和 “集合”两种性质,集合性质保证有序集合只会包含各不相同的成员,而有序性质则保证了有序集合中的所有成员都会按照特定的顺序进行排列;在一般情况下,有序集合成员的大小...

    有序集合说明

    Redis的有序集合(sorted set),同时具有“有序”和“集合”两种性质,这种数据结构中的每个元素都由一个成员和和一个与成员相关联的分值组成,其中成员以字符串方式存储,而分值则以64位双精度浮点数格式存储。与集合一样,有序集合中的每一个成员都是独一无二的,同一个有序中不会出现重复的成员,与此同时有序集合的成员将按照他们各自的分值大小进行排序:比如分值为3.14的成员将小于分值为10.24的成员,而分值为10000的成员也会小于分支为10086的成员有序集合的分值,除了可以是数字之外,还可以是字符串"+inf "或者"-inf ",这两个特殊分值分别用于表示无穷大和无穷小。

    记录薪水数据的有序集合


           需要注意的是,虽然同一个有序集合,不能存储相同的成员,但不同成员的分值却可以是相同的。当两个或多个成员拥有相同的分值时,Redis将按照这些成员在字典中的大小对其进行排序:举个例子,如果成员apple和成员zero都拥有相同的分值100,那么Redis将认为成员apple小于成员zero,这是因为在字典序中字母a开头的单词要小于字母Z开头的单词。

    记录水果价格的有序集合


           有序集合是Redis提供的所有数据结构中最为灵活的一种,它可以以多种不同的方式存取数据,比如根据成员获取分值,根据分值获取成员,根据成员的排名获取成员,根据指定的分值范围,获取不同成员等。

    有序集合通用命令使用

    zadd:添加或更新成员

    通过zadd命令用户,可以向有序集合中添加一个或多个新成员

    zadd sorted-set score member [score member ...]

    默认情况下,zadd命令将返回成功添加的新成员数量作为返回值。

    上图示例,对于不存在的键salary执行zadd命令,将创建出一个包含4个成员的有序集合。

    通过zadd命令新创建出来的有序集合salary


    zadd命令除了可以向有序集合添加新成员之外,还可以对有序中已经存在的成员分值进行更新:在默认情况下,如果用户在执行zadd命令时,给定成员已经存在了分值并且给定的分值和成员现有的分值并不相同,那么zadd命令将使用给定的新分值去覆盖现有的旧分值。

    举个例子,对于有序集合salary,之前tom薪水为2000,我们执行zadd salary 5000 tom命令

    命令返回为0,因为这是一次更新操作,没有添加任何新成员,所以命令返回0

    通过zadd命令更新后的有序集合salary

    从Redis3.0.2版本开始,Redis允许用户在执行的命令是通过使用可选的xx选项或者nx选项来显示的指示命令只执行更新操作或只执行添加操作

    zadd sorted-set [xx|nx] score member [score member ...]
    这两个选项的功能如下:

    • 在给定xx选项的情况下,zadd命令只会对给定成员当中已经存在于有序结合的成员进行更新,而那些不存在于有序集合的给定成员,则会被忽略。换句话说带有xx选项的zadd命令只会对有序集合已有的成员进行更新,而不会向有序集合添加任何新成员。
    • 在给定nx选项的情况下,命令只会把给定成员当中不存在于有序集合的成员添加到有序集合里面,而那些已经存在于有序集合中的给定成员则会被忽略。换句话说这有nx选项的命令只会向有序集合添加新成员,而不会对已有的成员进行任何更新。

    示例,对于集合salary,执行以下命令只会将成员jack的分值从原来4000调整为4500,而命令中出现的新成员bob则不会被添加到有序集合中

    如果对集合salary,执行以下命令,则只会将原先集合salary中不存在的成员bob添加到集合中,而已经存在的成员jack的分值并不会被改变。

    在默认情况下,zadd命令会返回新添加成员的数量作为返回值。但是从Redis3.0.2版本开始,用户可以通过给定ch选项,让命令返回参数被修改(changed)成员的数量作为返回值。

    zadd sorted-set [ch] score member [score member ...]
    "被修改成员"指的是新添加的有序集合的成员以及分值被更新的成员。

    示例,对于有序集合salary,执行命令 zadd salary ch 3500 peter 4000 bob 9000 david

    zrange 命令为查看集合所有键值对的命令,后面会说到。通过使用zrange命令查看salary集合成员,可以看到,被修改的成员为bob和david,前者的值从3800变为了4000,后者则被添加到有序集合中。与此相反,成员peter已经存在于集合中,并且它的值已经是3500,所以命令没有对它做任何修改。


    zrem:移除指定的成员

    通过使用zrem命令,用户可以从有序集合中移除指定的一个或多个成员,以及这些成员关联的分值。

    zrem sorted-set member [member ...]
    zrem命令会返回被移除成员的数量作为返回值

    示例,通过下面命令,可以删除有序集合salary中一个成员

    通过执行以下命令,可以删除有序集合中两个成员


    如果用户给定的某个成员并不存在于有序集合中,那么zrem命令将会自动忽略该成员。

    比如,执行以下命令并不会导致salary中任何元素被移除,因为这些元素都不存在与集合中


    zscore:获取成员的分值

    通过使用zscore命令,用户可以获取与给定成员相关联的分值。

    zscore sorted-set member

    有序集合salary

    示例,对于有序集合salary,可以通过zscore来获取各成员的分值情况

    上图中,通过zscore获取一个成员的分值时成功了,当一次想要获取两个成员的分值时就失败了。说明,zscore命令只可以获取单成员的分值,无法一次性获取多个成员的分值。

    如果用户给定的有序集合并不存在,或者有序集合中并未包含给定的成员,那么zscore命令将返回空值。


    zincrby:对成员的分值执行自增或自减操作

    通过使用zincrby命令,用户可以对有序集合中给定成员的分值执行自增操作或其加上指定的增量。

    zincrby sorted-set increment member

    zincrby命令在执行完自增操作后,将返回给定成员当前的分值

    上图示例,有序集合salary中peter值为3900,只用zincrby salary 300 peter,成功将peter分值增加到4200。
    因为Redis只提供了对分值执行自增操作的zincrby命令,并没有提供相对应的分值自减操作的命令。所以如果我们需要减少一个成员的分值时,那么可以将一个负数增量传递给zincrby命令,从而达到对分值执行自减操作的目的。

    上图示例,给peter增加-200,即给peter分值减去200,得到结果4000。
    如果用户在执行zincrby命令时,给定成员并不存在于有序集合中或者给定的有序集合并不存在,那么zincrby命令将直接把给定的成员添加到有序集合中,并把给定的增量设置为该成员的分值,效果相当于执行zadd命令。

    上图中,salary中原本不存在成员liuche,通过命令zincrby salary 2000 liuche ,给liuche做分值增加,最终效果即给集合中添加(zadd salary 2000 liuche )liuche这个成员及其对应分值。

    上图中,blog-visit是个不存在的有序集合,通过命令zincrby blog-visit 123456 magi1201 做增加操作,最终相当于将magi1201这个成员添加(zadd blog-visit 123456 magi1201)到集合中。


    zcard:获取有序集合大小

    通过执行zcard命令,用户可以获取有序集合的基数,即有序集合包含的成员数量

    zcard sorted-set

    上图示例中,通过zrange命令可以查看到salary集合中有5个元素,blog-visit集合中有1个元素,通过zcard命令可以获取到salary的元素个数5,获取blog-visit元素个数1。
    如果给定的有序集合并不存在,那么zcard命令将返回0作为结果


    zrank、zrevrank:获取成员在有序集合中的排名

    通过zrank命令和或者zrevrank命令,用户可以获取给定成员在有序集合中的排名

    zrank sorted-set member

    zrevrank sorted-set member
    其中zrank命令返回的是成员的升序排列排名及成员的按照分值,从小到大进行排列时的排名;而zrevrank命令返回的则是成员的降序排列排名及成员按照分值从大到小进行排序时的排名。有序集合中排名从0开始,最大序号为集合大小减1。

    有序集合salary中各成员在zrank命令和zrevrank命令下的排名值

    举个例子,上图中salary集合中各元素,通过zrank和zrevrank来获取一下排名信息

    上图中,展示了zrank和zrevrank命令分别获取salary集合中元素排名情况。其中,成员bob和peter的分值都为4000,他们的排名bob在peter之前。有序集合中,如果成员分值相同,分值相同的成员之间按照字典序排列。
    如果用户给定的有序集合并不存在,或者用户给定的成员并不存在于有序结合当中,那么zrank命令和zrevrank命令将会返回一个空值。


    zrange、zrevrange:获取指定索引范围内的成员

    通过zrange命令和zrevrange命令,用户可以以升序排列或者降序排列凡是,从有序集合中获取指定索引范围内的成员

    zrange sorted-set start end

    zrevrange sorted-set start end

    其中zrange命令用于获取按照分值大小实施升序排列的成员;而zrevrange命令用于获取按照分值大小实施降序排列的成员。命令中start索引和end索引指定的是闭区间范围,就是说,位于start和end两个位置的成员会包含在命令返回的结果中。如果start和end相同,则获取到一个成员。

    有序集合元素索引标识

    依然使用salary做示例,获取salary中排名0到3的成员

    取salary集合中,降序1到3的成员

    下图示例,开始和结束索引均为1 的情况,则获取排名为1的一个成员。

    与列表的lrange命令类似,有序集合zrange命令和zrevrange命令除了正数索引外,也可以使用负数索引。

    上图示例,通过使用负数索引获取salary集合中-4到-2位置上升序的元素列表和降序的元素列表。

    如果想要以升序或者降序方式获取salary有序集合的所有元素,那么只须要将起始索引设置为0,将结束索引设置为-1,然后调用zrange命令或zrevrange命令。

    上图示例,分别通过zrange和zrevrange命令获取salary集合的所有元素。

    如果用户给的索引超出了有效索引的范围,该如何处理,分情况:

    • 起始start和结束end索引之间无有效索引,返回空集合(empty list or set);
    • 如果起始start和结束end索引之间有有效索引位置,则Redis自动修正起始和结束索引位置,并返回有效索引范围内的成员。

    上图示例中,有序集合salary中包含5个元素,其正数索引为0到4。通过zrange salary 0 10 获取0到10位置上索引时,因为10超过了有效索引4,Redis自动将10修正为4,并返回0到4位置上成员信息;通过zrange salary 11 15获取元素时,因为11和15之间不存在有效索引,所以,返回空列表 empty list or set

    默认情况下,zrange命令和zrevrange命令只会返回指定索引范围内的成员,如果用户想要在获取到成员的同时也获取到与之对应的分值,那么可以在调用zrange命令或zrevrange命令的时候,给定可选的withscores选项

    zrange sorted-set start end withscores

    zrevrange sorted-set start end withscores

    下面示例,展示如何获取有序集合中成员和其关联的分值

    上面示例中,获取升序的集合所有成员和其对应的分值,其中奇数行的为成员信息,偶数行为其对应的分值信息。

    如果用户给定的有序集合不存在,那么zrange命令和zrevrange命令将返回一个空列表


    zrangebyscore、zrevrangebyscore:获取指定分值范围内的成员

    通过使用zrangebyscore命令或zrevrangebyscore命令,用户可以升序排列或者降序排列的方式获取有序集合中分值介于指定范围内的成员。

    zrangebyscore sorted-set min max

    zrevrangebyscore sorted-set max min

    命令的min参数和max参数分别用于指定用户想要获取的成员的最小分值和最大分值。不过需要注意的是zrangebyscore命令和zrevrangebyscore 命令能接受min参数和max参数的顺序正好相反,zrangebyscore命令先接受min参数,然后再接受max参数;而zrevrangebyscore命令则是先接受max参数后接受min参数。

    依然使用salary集合做示例,以升序方式获取分值在2000到4000之间的成员

    以降序方式获取5000到4000之间的成员

    与zrange命令和zrevrange命令类似,zrangebyscore命令和zrevrangebyscore命令也可以通过在执行时给定可选withscores选项来同时获取成员极其分值。

    上图示例,展示了zrangebyscore和zrevrangebyscore两个命令在获取成员时同时获取分值的使用。

    默认情况下,zrangebyscore命令和zrevrangebyscore命令会直接返回给定分值范围内的所有成员,但如果范围内的成员数量较多,或者我们只须要范围内的其中一部分成员,那么可以使用可选的limit选项来限制命令返回的成员数量

    zrangebyscore sorted-set min max [limit offset count]

    zrevrangebyscore sorted-set max min [limit offset count]

    limit 选项接受offset和count两个参数作为输入,其中offset参数用于指定命令在返回结果之前须要跳过的成员数量,而count参数则用于指示命令最多可以返回多少个成员。

    举个例子,在salary集合中,获取分值介于3000~5000的第一个成员,可以执行如下命令

    上面示例中,offset参数值为0,表示命令不须要跳过任何成员;而count参数的值为1,表示命令只须要返回1个成员结果。

    如果想要以升序方式,获取分值介于3000~6000的第二个和第三个成员,可以执行如下命令

    上面示例中,offset参数的值为1,表示命令须要跳过指定分值范围内的第一个成员,count参数值为2,表示命令在跳过第一个成员后,获取接下来两个成员,即指定分值范围内的第二个和第三个成员。

    默认情况下,zrangebyscore命令和zrevrangebyscore命令接受的分值范围都是闭区间分值范围。即,分值等于用户给定最大分值和最小分值的成员也会被包含在结果当中。如果用户想要的是开区间而不是闭区间,可么可以在给定分值范围时,在分值参数的前面加上一个单括号 ( ,这样,具有给定分值的成员就不会出现在命令返回的结果当中。

    例如,获取分值大于3000且小于5000的成员,结果中不包含3000和5000的成员

    比对上面示例可以看到,在使用开区间标识符号 ( 的执行中,没有返回分值为5000的成员,而默认的不带开区间符号的执行中,返回分值为5000的成员。

    zrangebyscore命令和zrevrangebyscore命令的min参数和max参数除了可以是普通分值或者带有 ( 符号的分值之外,还可以是特殊值 +inf 或者 -inf ,前缀用于表示无穷大,后者表示无穷小。当我们只想定义分值范围的上限或者下限,而不是同时定义分值范围的上限和下限时,+inf 和 -inf 就可以派上用场。

    比如,如果想要获取salary有序集合中所有分值小于5000的成员,可以执行如下命令

    这个命令执行中,只定义了分值范围的上限,而没有定义分值范围的下限,因此命令将返回有序集合中所有分值低于给定上限的成员。

    如果我们想要获取salary有序集合中所有分值大于4000的成员,那么可以执行如下命令

    上面示例中,只定义了分值下限,没有定义分值上限,因此命令将返回所有分值高于给定下限的成员。


    zcount:统计指定分值范围内的成员数量

    通过使用zcount命令,用户可以统计出有序集合中分值介于指定范围之内的成员数量。

    zcount sorted-set min max

    下面示例,展示zcount获取salary集合中,分值2000到4000的成员数量,可以辅助zrangebyscore比对,zrangebyscore获取到的数量和zcount获取到数量值应该是相同的。

    zcount命令接受的分值范围格式和zrangebyscore命令接受的分值范围格式完全相同:用户可以在执行zcount命令时,使用 +inf 表示无穷大值,使用 -inf 表示无穷小值,或者使用单括号 ( 定义开区间分值范围。

    举个例子,如果要获取分值小于5000的成员有多少个,则须要执行代码 zcount salary -inf (5000


    zremrangebyrank:移除指定排名范围内的成员

    zremrangebyrank命令可以从升序排列的有序集合中移除位于指定排名范围内的成员,然后返回被移除成员的数量

    zremrangebyrank sorted-set start end

    与 Redis的其他很多范围命令一样,zremrangebyrank命令接受的也是一个闭区间范围,即排名为start和end的成员也将被移除。

    举个例子,移除salary中排名1到3的3个元素

    删除1到3位置3个元素后,有序集合salary仅剩余2个元素了。
    传给zremrangebyrank命令的排名参数除了可以是正数之外,还可以是负数。

    例如,如果要删除salary中排名倒数前3的成员,只须要执行 zremrangebyrank salary -3 -1 即可。


    zremrangebyscore:移除指定分值范围内的成员

    zremrangebyscore命令可以从有序集合中移除位于指定分值范围内的成员,并在移除操作执行完毕返回被移除的成员的数量。

    zremrangebyscore sorted-set min max

    下图示例,移除分值在2000到3000范围内的成员

    zremrangebyscore命令接受的分值范围与zrangebyscore命令和zcount命令接受的分值范围一样,都默认为闭区间范围,但用户可以使用  ( 符号定义开区间,或者使用 +inf 和 -inf 表示正无穷大和负无穷大。

    下图示例,移除分值大于3500且小于等于4500的成员,有2个元素被移除


    zunionstore、zinterstore:有序集合的并集和交集运算

    用户可以通过zunionstore和zinterstore命令来操作有序集合的并集运算和交集运算。

    zunionstore destination numbers sorted-set [sorted-set ...]

    zinterstore destionation numbers sorted-set [sorted-set ...]

    其中,命令的numbers参数用于指定参与计算的有序集合数量,之后的一个或多个sorted-set参数则用于指定参与计算的各个有序集合的键,计算得出的结果会存储到destination参数指定的键中。zunionstore命令和zinterstore命令都会返回计算结果包含的成员数量作为返回值。

    举个例子,有序集合zs1 和 zs2,可以通过执行zunionstore命令计算出它们的并集,并将结果存储到zs3中

    上面有序集合中,zs3中成员c的分值为zs1中和zs2中两个c的分值之和。

    有序集合zs1和zs2执行zunionstore命令图示

    下面看一下,如果对zs1和zs2执行并集zinterstore操作,将结果保存到zs4中

    上图示例中,交集计算时,交集成员c的分值时根据zs1和zs2两个有序集合中成员c分值相加得来的。

    Redis为zunionstore命令和zinterstore命令提供了可选的aggregate选项,用户可以决定使用哪个聚合函数来计算结果有序集合成员的分值。

    zunionstore destination numbers sorted-set [sorted-set ...] [aggregate sum|min|max]

    zinterstore destination numbers sorted-set [sorted-set ...] [aggregate sum|min|max]

    aggregate选项的值可以是sum、min或者max中的一个,各聚合函数作用见下表

    聚合函数作用
    sum把给定有序集合中所有相同成员的分值都加起来,它们的和就是该成员在结果有序集合中的分值
    min从给定有序集合所有相同成员的分值中选出最小的分值,并把它用作该成员在结果有序集合中的分值
    max从给定有序集合所有相同成员的分值中选出最大的分值,并把它用作该成员在结果有序集合中的分值

    举个例子,对于有序集合ss1 、ss2 和 ss3,使用sum作为聚合函数进行交集运算,将得出一个结果为12的成员 a 

    结果值12 即为 2、6、4这三个值相加得出来

    使用min作为聚合函数进行交集运算,将得出一个分值为2的成员a

    同样,使用max作为聚合函数进行交集运算,将得到一个分值为6的成员a

    在没有显式使用aggregate选项指定聚合函数的情况下,zunionstore命令和zinterstore命令默认使用sum作为聚合函数。

    即以下两条并集计算命令具有相同效果:

    zunionstore destination numbers sorted-set [sorted-set ...] 

    zunionstore destination numbers sorted-set [sorted-set ...] aggregate sum

    以下两条交集计算命令具有相同效果:

    zinterstore destination numbers sorted-set [sorted-set ...] 

    zinterstore destination numbers sorted-set [sorted-set ...] aggregate sum

    在默认情况下,zunionstore和zinterstore将直接使用给定有序集合的成员分值去计算结果有序集合的成员分值,但是在有需要的情况下,用户也可以通过可选的weights参数为各个给定有序集合的成员分值设置权重

    zunionstore destination numbers sorted-set [sorted-set ...] [weights weight [weight ...]]

    zinterstore destination numbers sorted-set [sorted-set ...] [weights weight [weight ...]]

    在使用weights选项时,用户须要给每个给定的有序集合分别设置一个权重,命令会将这个权重与成员的分值相乘,得出成员的新分值,然后执行聚合运算;与此相反,如果用户在使用weights选项时,不想改变某个给定有序集合的分值,那么之需要将那个有序集合的权重设置为1即可。

    上图示例,通过对ss1、ss2和ss3 三个有序集合设置权重,后操作聚合函数执行交集运算,得出结果26

    26的值即为 ss1中a值3乘以对应权重3,加上ss2中a值6乘以对应权重2,加上ss3中a值4乘以对应权重2 之和。

    zunionstore和zinterstore除了可以使用有序集合(sorted-set)作为输入外,还可以使用集合(set)作为输入:默认情况下,这两个命令将把给定的集合看做所有成员的分值都为1的有序集合进行计算。如果有需要,用户也可以使用weights选项来改变给定集合的分值。比如,如果你希望某个集合所有成员的分值都被看做10而不是1,那么只须要在执行命令时把那个集合的权重设置为10即可。


    有序集合字典序命令使用

    在Redis有序集合中,对于拥有不同分值的有序集合成员来说,成员的大小将由分值决定,至于分值相同的成员,它们的大小则由该成员在字典序中的大小决定。这种排列规则的一个特例是,当有序集合中所有的成员都拥有相同的分值时,有序集合的成员将不再根据分值进行排序,而是根据字典序进行排序。在这种情况下,有序集合通用命令部分的zrangebyscore、zcount和zremrangebyscore等命令将不再适用。

    为了让用户可以对字典序排列的有序集合执行类似zrangebyscore这样的操作,Redis提供了相应的zrangebyscorelex、zrevrangebylex、zlexcount和zremrangebylex命令,这些命令可以分别对字典序排列的有序集合执行升序排列的范围获取操作、降序排列的范围获取操作、统计位于字典序指定范围内的成员数量以及移除位于字典序指定范围内的成员。

    为方便字典序有序集合示例操作,提供一个有序集合words,其中包含元素如下

    字典序集合words

    zrangebylex、zrevrangebylex:返回指定字典序范围内的成员

    用户可以通过使用zrangebylex命令,从字典序排列的有序集合中获取位于指定范围内的成员

    zrangebylex sorted-set min max

    命令的min参数和max参数用于指定用户想要获取的字段序范围,他们的值可以是以下四种值之一:

    • 带有 [ 符号的值表示在结果中包含与指定值具有同等字典序大小的成员
    • 带有 ( 符号的值表示在结果中不包含与给定值具有同等字典序大小的成员
    • 加号 + 表示无穷大
    • 减号 - 表示无穷小

    举个例子,对于字典序集合words来说,如果想要通过zrangebylex命令获取words有序集合包含的所有成员,那么只需要将min参数的值设置为-,max参数的值设置为+即可

    如果想要获取words集合中所有以a开头的成员,那么只须要将min参数的值设置为 [a,max参数的值设置为 (b 即可

    如果想要获取words集合中所有字典序小于字母c的成员,那么只须要将min参数的值设置为 -,max参数值设置为 (c 即可

    zrevrangebylex命令是逆序版的zrangebylex命令,它会以逆字典序的方式返回指定范围内的成员

    zrevrangebylex sorted-set min max

    zrevrangebylex命令接受min和max的参数顺序与zrangebylex命令正好相反,zrevrangebylex命令是先接受max参数,然后再接受min参数。此外,zrevrangebylex和zrangebylex两个命令的min参数和max参数能够接受的值是完全相同的。

    举个例子,通过你字典序方式返回有序集合中所有以字母a 和 b 开头的成员

    上图示例中,当给逆字典序min位置为 a 时,出现命令报错,印证字典序中min参数和max参数必须是指定值的规定。


    zlexcount:统计位于字典序指定范围内的成员数量

    对于按照字典序排列的有序集合,用户可以使用zlexcount命令统计有序集合中位于字典序指定范围内的成员数量

    zlexcount sorted-set min max

    zlexcount命令的min参数和max参数的格式与zrangebylex命令接受的min参数和max参数格式完全相同

    举个例子,获取有序集合words中以字母a开头的成员数量


    zremrangebylex:移除位于字典序指定范围内的成员

    对于按照字典序排列的有序集合,用户可以使用zremrangebylex命令移除有序集合中位于字典序指定范围内的成员

    zremrangebylex sorted-set min max 

    这个命令的min参数和max参数格式与zrangebylex命令以及zlexcount命令接受的min参数和max参数的格式完全相同。zremrangebylex命令在移除用户指定的成员之后,将返回被移除成员的数量作为命令的返回值。

    举个例子,移除有序集合words中以字母b 开头的成员

    移除后,words中仅剩余以a 和 c 开头的成员

    有序集合使用总结

    1、有序集合同时拥有“有序” 和 “集合”两种性质,集合性质保证有序集合只会包含各不相同的成员,而有序性质则保证了有序集合中的所有成员都会按照特定的顺序进行排列

    2、在一般情况下,有序集合成员的大小由分值决定,而分值相同的成员的大小则由成员在字典序中的大小决定。

    3、成员的分值除了可以是数字外,还可以是表示无穷大的 +inf 或者表示无穷小的 -inf 。

    4、zadd命令,可以通过给定可选项类决定执行添加操作或是执行更新操作

    5、因为Redis只提供了对成员分值执行加法计算的zincrby命令,没有提供相应的减法计算命令,所以只能通过zincrby命令传入负数增量来对成员分值执行减法计算

    6、zinterstore命令和zunionstore命令除了可以使用有序集合作为输入外,还可以使用集合作为输入。在默认情况下,这两个命令会把集合的成员看作分值为1的有序集合成员来计算。

    7、当有序集合的所有成员都拥有相同的分值时,用户可以通过zrangebylex、zlexcount、zremrangebylex等命令,按照字典序对有序集合中的成员进行操作,字典序有序集合命令中都有lex字样,lex为字典单词(lexico) 的简写。

     

     

     




     

     

    展开全文
  • 在JAVA中使用Redis有序集合类型的常用命令的演示及一个简单的实战项目: 具备增删改查和分类查找及按浏览量排序的商品管理功能
  • Redis有序集合

    千次阅读 2019-05-15 20:31:36
    文章目录一、有序集合介绍二、常用命令介绍三、有序集合数据结构为什么有序集合需要同时使用跳跃表和字典来实现?采用哪种编码由什么决定?压缩列表压缩列表节点的构成跳跃表 一、有序集合介绍 有序集合,顾名思义,...

    一、有序集合介绍

    有序集合,顾名思义,元素有序且唯一的集合,满足有序性、唯一性、确定性,sorted set、zset都是指的有序集合。

    二、常用命令介绍

    有序集合共有25个相关命令,这里只介绍常用的命令,其他的可以去官方文档查看。

    添加元素

    ZADD key [NX|XX] [CH] [INCR]score member [score member …]

    key:有序集合的键;

    NX:不存在才添加该元素,类似HashMap的putIfAbsent;

    XX:存在时才添加元素,也就是覆盖;

    CH:返回值将会是元素产生改变的数量,不加这个选项只改变分数是不会计数的;

    INCR:在原分数的基础上加上新的分数,而不是覆盖;

    score:元素的分数,分数越小,排名越靠前,排名从0开始,分数相同的情况下再按照字典序进行排序;

    member:元素成员

    元素加分

    ZINCRBY key increment member

    increment:待加分数

    等同于ZADD key INCRY score member

    查询元素个数

    ZCARD key

    统计分数范围内的元素个数

    ZCOUNT key min max

    min:最小分数

    max:最大分数

    使用示例:

    分数在负无穷到正无穷范围内的元素个数,也就是所有元素个数,效果同ZCARD

    ZCOUNT myzset -inf +inf

    统计分数大于1小于等于3的元素个数

    ZCOUNT myzset (1 3

    按排名范围查找

    ZRANGE key start stop [WITHSCORES]

    start:开始索引

    stop:结束索引

    WITHSCORES:同时查询出分数

    使用示例:

    ZRANGE myset 0 -1 WITHSCORES

    按分数范围查找

    ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

    LIMIT offset count类似mysql的用法,不再赘述

    按排名倒序范围查询

    ZREVRANGE key start stop [WITHSCORES]

    按分数倒序范围查询

    ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

    查询元素分数

    ZSCORE key member

    查询元素排名

    ZRANK key member

    排名从0开始

    查询元素倒序排名

    ZREVRANK key member

    删除元素

    ZREM key member [member …]

    按排名范围删除元素

    ZREMRANGEBYRANK key start stop

    按分数范围删除元素

    ZREMRANGEBYSCORE key min max

    lex相关的命令是分数相同时按字典序进行相关操作的命令,使用较少,这里不做介绍,有兴趣的自己查看官方文档:https://redis.io/commands

    三、有序集合数据结构

    Redis的五种数据结构在Redis的实现中都表示为一个Redis对象,对象包含了类型、编码、具体的数据等信息,有序集合的编码可以是zipList或skipList,ziplist编码的压缩列表对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员(member),而第二个元素则保存元素的分值(score)。

    压缩列表内的集合元素按分值从小到大进行排序,分值较小的元素被放置在靠近表头的方向,而分值较大的元素则被放置在靠近表尾的方向。

    在这里插入图片描述

    在这里插入图片描述

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

    typedef struct zset {
        zskiplist *zsl;
        dict *dict;
    } zset;
    

    在这里插入图片描述

    在这里插入图片描述

    为什么有序集合需要同时使用跳跃表和字典来实现?

    在理论上,有序集合可以单独使用字典或者跳跃表的其中一种数据结构来实现,但无论单独使用字典还是跳跃表,在性能上对比起同时使用字典和跳跃表都会有所降低。
    举个例子,如果我们只使用字典来实现有序集合,那么虽然以O(1)复杂度查找成员的分值这一特性会被保留,但是,因为字典以无序的方式来保存集合元素,所以每次在执行范围型操作一一一比如ZRANK、ZRANG等命令时,程序都需要对字典保存的所有元素进行排序,完成这种排序需要至少O(NlogN)时间复杂度,以及额外的O(N)内存空间(因为要创建一个数组来保存排序后的元素)。
    另一方面,如果我们只使用跳跃表来实现有序集合,那么跳跃表执行范围型操作的所有优点都会被保留,但因为没有了字典,所以根据成员查找分值这一操作的复杂度将从O(1)上升为O(logN)。因为以上原因,为了让有序集合的查找和范围型操作都尽可能快地执行,Redis选择了同时使用字典和跳跃表两种数据结构来实现有序集合。

    采用哪种编码由什么决定?

    当有序集合对象可以同时满足以下两个条件时,对象使用ziplist编码:

    • 有序集合保存的元素数量小于128个;
    • 有序集合保存的所有元素成员的长度都小于64字节;

    不能满足以上两个条件的有序集合对象将使用skiplist编码。

    以上两个条件的上限值可以通过zset-max-ziplist-entries(默认128)和zset-max-ziplist-value(默认64)来调整,当编码从ziplist转换成skiplist后,即便删除元素满足了上述条件,编码也不会从skiplist重新转换成ziplist。

    压缩列表

    压缩列表(zset)是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。

    压缩列表的构成

    压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构。一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。

    在这里插入图片描述

    压缩列表各个组成部分的说明

    属性类型长度用途
    zlbytesuint32_t4字节记录整个压缩列表占用的内存字节数:在对压缩列表进行内存重分配,或者计算zlend的位置时使用
    zltailuint32_t4字节记录压缩列表表尾节点距离压缩列表的起始地址有多少字节:通过这个偏移量,程序无须遍历整个压缩列表就可以确定表尾节点的地址
    zllenuint16_t2字节记录了压缩列表包含的节点数量:当这个属性的值小于UINT16_MAX(65535)时,这个属性的值就是压缩列表包含节点的数量;当这个值等于UINT16_MAX时,节点的真实数量需要遍历整个压缩列表才能计算得出
    entryX列表节点不定压缩列表包含的各个节点,节点的长度由节点保存的内容决定
    zlenduint8_t1字节特殊值0xFF(十进制255),用于标记压缩列表的末端

    压缩列表节点的构成

    每个压缩列表节点可以保存一个字节数组或者一个整数值,其中,字节数组可以是以下三种长度的其中一种:

    • 长度小于等于63(26一1)字节的字节数组;
    • 长度小于等于16383(2]4一1)字节的字节数组;
    • 长度小于等于4294967295(232一1)字节的字节数组;

    而整数值则可以是以下六种长度的其中一种:

    • 4位长,介于0至12之间的无符号整数
    • 1字节长的有符号整数;
    • 3字节长的有符号整数;
    • uint16_t类型整数;
    • uint32_t类型整数;
    • uint64_t类型整数。

    在这里插入图片描述

    previous_entry_length

    节点的属性以字节为单位,记录了压缩列表中前一个节点的长度。

    previousentry_length属性的长度可以是1字节或者5字节:

    • 如果前一节点的长度小于254字节,那么previous_entry_length属性的长度为1字节:前一节点的长度就保存在这一个字节里面。
    • 如果前一节点的长度大于等于254字节,那么previous_entry_length属性的长度为5字节:其中属性的第一字节会被设置为0xFE(十进制值254),而之后的四个字节则用于保存前一节点的长度。

    encoding

    节点的encoding属性记录了节点的content属性所保存数据的类型以及长度:

    • 一字节、两字节或者五字节长,值的最高位为00、01或者10的是字节数组编码:这种编码表示节点的content属性保存着字节数组,数组的长度由编码除去最高两位之后的其他位记录;
    • 一字节长,值的最高位以11开头的是整数编码:这种编码表示节点的content属性保存着整数值,整数值的类型和长度由编码除去最高两位之后的其他位记录。

    content

    每个压缩列表节点可以保存一个字节数组或者一个整数值,其中,字节数组可以是以下三种长度的其中一种:

    • 长度小于等于63(26一1)字节的字节数组;
    • 长度小于等于16383(2]4一1)字节的字节数组;
    • 长度小于等于4294967295(232一1)字节的字节数组;

    而整数值则可以是以下六种长度的其中一种:

    • 4位长,介于0至12之间的无符号整数
    • 1字节长的有符号整数;
    • 3字节长的有符号整数;
      在这里插入图片描述

    压缩列表操作API

    函数作用算法复杂度
    ziplistNew创建一个新的压缩列表O(1)
    ziplistPush创建一个包含给定值的新节点,并将这个新节点添加到压缩列表的表头或者表尾平均O(N),最坏O(N^2)
    ziplistlnsert将包含给定值的新节点插人到给定节点平均O(N),最坏O(N^2)
    ziplistlndex返回压缩列表给定索引上的节点O(N)
    ziplistFind在压缩列表中查找并返回包含了给定值的节点因为节点的值可能是一个字节数组,所以检查节点值和给定值是否相同的复杂度为O(N),而查找整个列表的复杂度则为O(N^2)
    ziplistNext返回给定节点的下一个节点O(1)
    ziplistPrev返回给定节点的前一个节点O(1)
    ziplistGet获取给定节点所保存的值O(1)
    ZiplistDelete从压缩列表中删除给定的节点平均O(N),最坏O(N^2)
    ziplistDeleteRange删除压缩列表在给定索引上的连续多个节点平均O(N),最坏O(N^2)
    ziplistBlobLen返回压缩列表目前占用的内存字节数O(1)
    ziplistLen返回压缩列表目前包含的节点数量节点数量小于65535时为O(1),大于65535时为O(N^2)

    跳跃表

    跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。

    跳跃表支持平均O(logN)、最坏O(N)复杂度的节点查找,还可以通过顺序性操作来批处理节点。
    在大部分情况下,跳跃表的效率可以和平衡树相媲美,并且因为跳跃表的实现比平衡树要来得更为简单,所以有不少程序都使用跳跃表来代替平衡树。
    Redis使用跳跃表作为有序集合键的底层实现之一,如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员(member)是比较长的字符串时,Redis就会使用跳跃表来作为有序集合键的底层实现。

    在这里插入图片描述

    跳跃表操作API

    函数作用时间复杂度
    zslCreate创建一个新的跳跃表O(1)
    zslFree释放给定跳跃表,以及表中包含的所有节点O(N),N为表的长度
    zsIInsert将包含给定成员和分值的新节点添加到跳跃表中平均O(logN),最坏O(N),N为跳跃表长度
    zsIDeIete删除跳跃表中包含给定成员和分值的节点平均O(logN),最坏O(N),N为跳跃表长度
    zslGetRank返回包含给定成员和分值的节点在跳跃表中的排位平均O(logN),最坏O(N),N为跳跃表长度
    zsIGetEIementByRank返回表在给定排位上的节点平均O(logN),最坏O(N),N为跳跃表长度
    zslIsInRange给定一个分值范围,有至少一个节点的分值在该范围内,返回1,否则返回0通过跳跃表的表头节点和表尾节点,这个检测可以用O(1)复杂度完成
    zsIFirstInRange给定一个分值范围,返回跳跃表中第一个符合这个范围的节点平均O(logN),最坏O(N),N为跳跃表长度
    zs1LastInRange给定一个分值范围,返回跳跃表中最后一个符合这个范围的节点平均O(logN),最坏O(N),N为跳跃表长度
    zs1De1eteRangeByScore给定一个分值范围,删除跳跃表中所有在这个范围之内的节点O(N),N为被删除节点数量
    zs1De1eteRangeByRank给定一个排位范围,删除跳跃表中所有在这个范围之内的节点O(N),N为被删除节点数量
    展开全文
  • Redis有序集合详解

    2020-03-20 17:32:19
    有序集合和集合类似,只是说它是有序的,和无序集合的主要区别在于每一个元素除了值之外,它还会多一个分数。分数是一个浮点数,在 Java 中是使用双精度表示的,根据分数,Redis 就可以支持对分数从小到大或者从大到...

    有序集合和集合类似,只是说它是有序的,和无序集合的主要区别在于每一个元素除了值之外,它还会多一个分数。分数是一个浮点数,在 Java 中是使用双精度表示的,根据分数,Redis 就可以支持对分数从小到大或者从大到小的排序。

    这里和无序集合一样,对于每一个元素都是唯一的,但是对于不同元素而言,它的分数可以一样。元素也是 String 数据类型,也是一种基于 hash 的存储结构。

    集合是通过哈希表实现的,所以添加、删除、查找的复杂度都是 0(1)。集合中最大的成员数为 2 的 32 次方减 1(40 多亿个成员),有序集合的数据结构如图所示。
    在这里插入图片描述

    有序集合是依赖 key 标示它是属于哪个集合,依赖分数进行排序,所以值和分数是必须的,而实际上不仅可以对分数进行排序,在满足一定的条件下,也可以对值进行排序。

    Redis基础命令

    有序集合和无序集合的命令是接近的,只是在这些命令的基础上,会增加对于排序的操作,这些是我们在使用的时候需要注意的细节。

    下面讲解这些常用的有序集合的部分命令。有些时候 Redis 借助数据区间的表示方法来表示包含或者不包含,比如在数学的区间表示中,[2,5] 表示包含 2,但是不包含 5 的区间。

    Redis有序集合的部分命令
    在这里插入图片描述
    在这里插入图片描述

    在对有序集合、下标、区间的表示方法进行操作的时候,需要十分小心命令,注意它是操作分数还是值,稍有不慎就会出现问题。

    这里命令比较多,也有些命令比较难使用,在使用的时候,务必要小心,不过好在我们使用 zset 的频率并不是太高,下面是测试结果——有序集合命令展示。

    在这里插入图片描述

    spring-data-redis对有序集合的封装

    在 Spring 中使用 Redis 的有序集合,需要注意的是 Spring 对 Redis 有序集合的元素的值和分数的范围(Range)和限制(Limit)进行了封装,在演示如何使用 Spring 操作有序集合前要进一步了解它的封装。

    先介绍一个主要的接口——TypedTuple,它不是一个普通的接口,而一个内部接口,它是 org.springframework.data.redis.core.ZSetOperations 接口的内部接口,它定义了两个方法,代码如下所示。

    public interface ZSetOperations<K,V>{
        ......
    public interface TypedTuple<V> extends Comparable<TypedTuple<V>< {
        V getValue();
    
        Double getScore();
    }
    ......
    }
    

    这里的 getValue() 是获取值,而 getScore() 是获取分数,但是它只是一个接口,而不是一个实现类。spring-data-redis 提供了一个默认的实现类—— DefaultTypedTuple,同样它会实现 TypedTuple 接口,在默认的情况下 Spring 就会把带有分数的有序集合的值和分数封装到这个类中,这样就可以通过这个类对象读取对应的值和分数了。

    Spring 不仅对有序集合元素封装,而且对范围也进行了封装,方便使用。它是使用接口 org.springframework.data.redis.connection.RedisZSetCommands 下的内部类 Range 进行封装的,它有一个静态的 range() 方法,使用它就可以生成一个 Range 对象了,只是要清楚 Range 对象的几个方法才行,为此我们来看看下面的伪代码。

    //设置大于等于min
    public Range gte(Object min)
    //设置大于min
    public Range gt(Object min)
    //设置小于等于max
    public Range lte(Object max)
    //设置小于max
    public Range lt(Object max)
    

    这 4 个方法就是最常用的范围方法。下面讨论一下限制,它是接口 org.springframework.data.redis.connection.RedisZSetCommands 下的内部类,它是一个简单的 POJO,它存在两个属性,它们的 getter 和 setter 方法,如下面的代码所示。

    // ......
    public interface RedisZSetCommands {
        // ......
    public class Limit {
        int offset;
        int count;
    //setter和getter方法
    }
    //......
    }
    

    通过属性的名称很容易知道:offset 代表从第几个开始截取,而 count 代表限制返回的总数量。

    通过 Spring 操作有序集合

    我们讨论了 spring-data-redis 项目对有序集合的封装。在测试代码前,要把 RedisTemplate 的 keySerializer 和 valueSerializer 属性都修改为字符串序列化器 StringRedisSerializer,测试代码如下所示。

    public static void testZset() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
                    "applicationContext.xml");
        RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
        // Spring提供接口 TypedTuple操作有序集合
        Set<TypedTuple> set1 = new HashSet<TypedTuple>();
        Set<TypedTuple> set2 = new HashSet<TypedTuple>();
        int j = 9;
        for (int i = 1; i <= 9; i++) {
            j--;
            // 计算分数和值
            Double score1 = Double.valueOf(i);
            String value1 = "x" + i;
            Double score2 = Double.valueOf(j);
            String value2 = j % 2 == 1 ? "y" + j : "x" + j;
            // 使用 Spring 提供的默认 TypedTuple--DefaultTypedTuple
            TypedTuple typedTuple1 = new DefaultTypedTuple(value1, score1);
            set1.add(typedTuple1);
            TypedTuple typedTuple2 = new DefaultTypedTuple(value2, score2);
            set2.add(typedTuple2);
        }
        // 将元素插入有序集合zset1
        redisTemplate.opsForZSet().add("zset1", set1);
        redisTemplate.opsForZSet().add("zset2", set2);
        // 统计总数
        Long size = null;
        size = redisTemplate.opsForZSet().zCard("set1");
        // 计分数为score,那么下面的方法就是求 3<=score<=6的元素
        size = redisTemplate.opsForZSet().count("zset1", 3, 6);
        Set set = null;
        // 从下标一开始截取5个元素,但是不返回分数,每一个元索是String
        set = redisTemplate.opsForZSet().range("zset1", 1, 5);
        printSet(set);
        // 截取集合所有元素,并且对集合按分数排序,并返回分数,每一个元素是TypedTuple
        set = redisTemplate.opsForZSet().rangeWithScores("zset1", 0, -1);
        printTypedTuple(set);
        // 将zset1和zset2两个集合的交集放入集合inter_zset
        size = redisTemplate.opsForZSet().intersectAndStore("zset1", "zset2","inter_zset");
        // 区间
        Range range = Range.range();
        range.lt("x8");// 小于
        range.gt("x1"); // 大于
        set = redisTemplate.opsForZSet().rangeByLex("zset1", range);
        printSet(set);
        range.lte("x8"); // 小于等于
        range.gte("xl"); // 大于等于
        set = redisTemplate.opsForZSet().rangeByLex("zset1", range);
        printSet(set);
        // 限制返回个数
        Limit limit = Limit.limit();
        // 限制返回个数
        limit.count(4);
        // 限制从第五个开始截取
        limit.offset(5);
        // 求区间内的元素,并限制返回4条
        set = redisTemplate.opsForZSet().rangeByLex("zset1", range, limit);
        printSet(set);
        // 求排行,排名第1返回0,第2返回1
        Long rank = redisTemplate.opsForZSet().rank("zset1", "x4");
        System.err.println("rank = " + rank);
        // 删除元素,返回删除个数
        size = redisTemplate.opsForZSet().remove("zset1", "x5", "x6");
        System.err.println("delete = " + size);
        // 按照排行删除从0开始算起,这里将删除第排名第2和第3的元素
        size = redisTemplate.opsForZSet().removeRange("zset2", 1, 2);
        // 获取所有集合的元素和分数,以-1代表全部元素
        set = redisTemplate.opsForZSet().rangeWithScores("zset2", 0, -1);
        printTypedTuple(set);
        // 删除指定的元素
        size = redisTemplate.opsForZSet().remove("zset2", "y5", "y3");
        System.err.println(size);
        // 给集合中的一个元素的分数加上11
        Double dbl = redisTemplate.opsForZSet().incrementScore("zset1", "x1",11);
        redisTemplate.opsForZSet().removeRangeByScore("zset1", 1, 2);
        set = redisTemplate.opsForZSet().reverseRangeWithScores("zset2", 1, 10);
        printTypedTuple(set);
    }
    
    /**
    * 打印TypedTuple集合
    * @param set
    * -- Set<TypedTuple>
    */
    public static void printTypedTuple(Set<TypedTuple> set) {
        if (set != null && set.isEmpty()) {
            return;
        }
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            TypedTuple val = (TypedTuple) iterator.next();
            System.err.print("{value = " + val.getValue() + ", score = "
                    + val.getScore() + "}\n");
        }
    }
    
    /**
    * 打印普通集合
    * @param set普通集合
    */
    public static void printSet(Set set) {
        if (set != null && set.isEmpty()) {
            return;
        }
        Iterator iterator = set.iterator();
        while (iterator .hasNext()) {
        Object val = iterator.next();
        System. out.print (val +"\t");
        }
        System.out.println();
    }
    

    上面的代码演示了大部分 Spring 对有序集合的操作,并给出了比较清晰的注释,大家认真思考之后就能熟悉如何通过 Spring 操作有序集合了。

    展开全文
  • 本章节讲解Redis中集合和有序集合的常用命令、内部编码和使用场景
  • Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。 有序集合的成员是唯一的,但...
  • Java- 有序集合

    千次阅读 2020-09-04 13:00:07
    集合概述 什么是集合?有什么作用 所有的集合类和集合节后都在java.util包下 集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,集合当存储的都是java对象的内存地址。...有序集合 List Ar
  • java有哪些有序集合?

    千次阅读 2021-03-05 15:37:41
    Comparable接口Comparator接口SortedSet接口SortedMap接口在集合框架中有许多类具有排序功能,如:TreeSet类TreeMap类为了用“集合框架”的额外部分把排序支持添加到Java2 SDK,版本1.2,核心Java库作了许多更改。...
  • 程序是什么有序集合

    千次阅读 2021-07-23 03:11:21
    程序是为实现特定目标或解决特定问题而用计算机语言编写的命令有序集合,为进行某活动或过程所规定的途径。程序,香港和台湾对英文procedure的中文翻译,编程语言中的procedure在大陆翻译为“过程”,在港台和其他...
  • 我们通过redis的有序集合zset来实现简单的延迟队列,将消息数据序列化,作为zset的value,把消息处理时间作为score,每次通过zRangeByScore获取一条消息进行处理。 <?php class DelayQueue { protected $prefix...
  • 插入有序集合有序集合添加一个或多个成员,或者更新已存在成员的分数 语法:ZADD key score1 member1 score2 member2 ... 127.0.0.1:6379> zadd salary 10000 jim 8000 tom 12000 jimi 10000 hake ...
  • 有序集合和集合一样,都可以包含任意数量的、各不相同的元素( element),不同于集合的是,有序集合的每个元素都关联着一个浮点数格式的分 值(score),并且有序集合会按照分 值,以从小到大的顺序来排列有序集合...
  • Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。 常应用于排行榜,分数排名等场景。 不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序...
  • 集合 集合(set)类型也是用来保存多个的字符串元素,但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。如图2-22所示,集合user:1:follow包含着"it"、"music...
  • Java中的有序集合

    万次阅读 2019-09-07 19:58:52
    Java中的有序集合 面试的时候经常会被问道这样一个问题:Java集合中哪些是有序的,哪些不是? 我们通常的回答是List、LinkedHashMap、LinkedHashSet、TreeMap、TreeSet是有序的,List、LinkedHashMap、LinkedHashSet...
  • 目 录 Redis列表(list)常用命令数据结构Redis 集合(set)常用命令数据结构Redis哈希(Hash)常用命令数据结构Redis有序集合Zset(sorted set)常用命令数据结构跳跃表 Redis列表(list) 单键多值 Redis列表是简单...
  • Java实现Redis的有序集合(sortedset)命令

    千次阅读 2018-08-13 09:36:05
    *Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。 不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。 有序集合的成员是唯一的,...
  • redis之有序集合

    千次阅读 2018-03-26 09:09:27
    一、概述 有序集合和集合最大的区别在于有序二字,有序集合为每个元素关联一个分数(元素的分数可以相同)。 有序集合类型和列表的共同点 1、二者都是有序的 2、二者都一个获取某一个范围的元素 但是二者也有很...
  • 返回的是有序集合 zrevrange key start end 同上,返回结果是按score逆序的 zcard key 返回集合中元素的个数 zscore key member 返回给定元素对应的score zremrangebyrank key min max 删除集合中排名在给定区间的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 329,277
精华内容 131,710
关键字:

有序集合