Redis有序集合类型

今天我们继续学习Redis中的数据类型,今天我们学习有序集合类型,也是Redis中的5大数据类型中的最后一个。看名字,我们就知道,有序集合也是一种集合,并且这个集合还是有序的。那有序集合和列表有什么不同呢?因为列表也是有序的。 它们到底有什么不同呢?有序集合的有序和列表的有序是不同的。列表中的有序指的的是插入元素的顺序,和查询元素的顺序相同。而有序集合中的有序指的是它会为每个元素设置一个分数(score),而查询时可以通过分数计算元素的排名,然后在返回结果。因为有序集合也是集合类型,所以有序集合中也是不插入重复元素的,但在有序集合中分数则是可以重复,那如果在有序集合中有多个元素的分数是相同的,那么这些重复的元素的排名是怎么计算的呢?在下面的内容中我们在做详细说明。下面我们看一下列表、集合、有序集合的它们3个数据类型之间的区别。

数据结构是否允许重复元素是否有序有序实现方式应用场景
列表索引下标时间轴、消息队列
集合标签、社交
有序集合分数排行榜、社交

下面我们重点了解一下Redis中有序集合的相关命令。

命令

一、集合内

1.添加元素

zadd key [NX|XX] [CH] [INCR] score member [score member ...]

img

zadd命令也是有返回值的,返回值就是当前zadd命令成功添加元素的个数。除此之外,zadd命令还有很多其它的选填参数。下面我们详细了解一下:

  • nx: 元素必须不存在时,才可以设置成功。
  • xx: 元素必须存在时,才可以设置成功。
  • ch:
  • incr: 对score做增加。

备注:由于有序集合相比集合提供了排序字段,正是因为如此也付出了相应的代价,,zadd的时间复杂度为O(log(n)),sadd的时间复杂度为O(1)。


2.计算成员个数

zcard key

img


3.计算某个成员的分数

zscore key member

img

在使用zscore命令时,如果key不存在,或者元素不存在时,该命令返回的都是(nil)。


4.计算成员的排名

zrank key member
zrevrank key member

img

zrank命令是从分数低到高排名,而zrevrank命令则恰恰相反,从高到低排名。有一点要特别注意zrank和zrevrank命令与zscore命令不同的前者是通过分数计算出最后的排名,而后者则是直接返回当前元素的分数。


5.删除元素

zrem key member [member ...]

img

返回的结果为成功删除元素的个数,因为zrem命令是支持批量删除的。


6.增加元素分数

zincrby key increment member

img

虽然zincrby命令是增加元素分数的,但我们也可以指定负数,这样当前元素的分数,则会相减。


7.返回指定排名范围的元素

zrange key start stop [WITHSCORES]
zrevrange key start stop [WITHSCORES]

img

zrange命令是通过分数从低到高返回数据,而zrevrange命令是通过分数从高到低返回数据。如果执行命令时如果添加了WITHSCORES可选参数,而返回数据时会返回当前元素的分数。


8.返回指定分数范围的元素

zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]

img

min和max参数同时还支持开区间(小括号)和闭区间(中括号),同时我们还可以用-inf和+inf参数代表无限小和无限大。

img

备注:闭区间的没有验证成功,执行时会报错,这个有待验证。


9.返回指定分数范围元素个数

zcount key min max

img


10.删除指定指定排名内的升序元素

zremrangebyrank key start stop

img


11.删除指定分数范围元素

zremrangebyscore key min max

img


二、集合间操作

1.交集

zinterstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]

因为zinterstore命令参数比较多,所以下面我们详细了解一下:

  • destination:将交集的计算结果,保存到这个键中。
  • numkeys:需要做交集计算键的个数。
  • key [key ...]:需要做交集计算的键。
  • WEIGHTS weight:每个键的权重,在做交集计算时,每个键中的分数值都会乘以这个权重,默认每个键的权重为1。
  • AGGREGATE SUM|MIN|MAX:计算成员交集后,分值可以按照sum(和)、min(最小值)、max(最大值)做汇总,默认值为sum。

img
下面我们将权重设置为0.5,这样当计算交集后,有序集合中的元素分数将都会减半,并且使用max参数汇总。
img


2.并集

zunionstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]

img

zunionstore命令的相关参数和zinterstore命令相同。


时间复杂度

下面我们了解一下上述命令的时间复杂度。

命令时间复杂度
zadd key [NX/XX] [CH] [INCR] score member [score member ...]O(k*log(n)),k是添加元素的个数,n是当前有序集合元素个数
zcard keyO(1)
zscore key memberO(1)
zrank key memberO(log(n)),n是当前有序集合元素个数
zrevrank key memberO(log(n)),n是当前有序集合元素个数
zrem key member [member ...]O(k*log(n)),k是删除元素的个数,n是当前有序集合元素个数
zincrby key increment memberO(log(n)),n是当前有序集合元素个数
zrange key start stop [WITHSCORES]O(log(n) + k),k是要获取的元素个数,n是当前有序集合个数
zrevrange key start stop [WITHSCORES]O(log(n) + k),k是要获取的元素个数,n是当前有序集合个数
zrangebyscore key min max [WITHSCORES] [LIMIT offset count]O(log(n) + k),k是要获取的元素个数,n是当前有序集合个数
zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]O(log(n) + k),k是要获取的元素个数,n是当前有序集合个数
zcount key min maxO(log(n)),n是当前有序集合元素个数
zremrangebyrank key start stopO(log(n) + k),k是要删除的元素个数,n是当前有序集合个数
zremrangebyscore key min maxO(log(n) + k),k是要删除的元素个数,n是当前有序集合个数
zinterstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM/MIN/MAX]O(n k) + O(m log(m)),n是元素元素最小的有序集合元素个数,k是有序集合个数,m是结果集中元素个数
zunionstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM/MIN/MAX]O(n) + O(m * log(m)),n是所有有序集合元素个数和,m是结果集中元素个数。

内部编码

有序集合类型的内部编码有两种,它们分别是:

  • ziplist(压缩列表):当有序集合的元素个数小于128个(默认设置),同时每个元素的值都小于64字节(默认设置),Redis会采用ziplist作为有序集合的内部实现。
  • skiplist(跳跃表):当上述条件不满足时,Redis会采用skiplist作为内部编码。

备注:上述中的默认值,也可以通过以下参数设置:zset-max-ziplist-entries和zset-max-ziplist-value。


下面我们用以下示例来验证上述结论。

1.当元素个数比较少,并且每个元素也比较小时,内部编码为ziplist:

img


2.当元素个数超过128时,内部编码为skiplist。下面我们将采用python动态创建128个元素,下面为源码:

import redis

r = redis.Redis(host='127.0.0.1', port=6379)

if r.object('encoding', 'zsetkey') != None:
    print('Key为【zsetkey】的字节编码为【%s】' % r.object('encoding', 'zsetkey').decode('utf-8'))

for i in range(1, 600):
    r.zadd('zsetkey',i,1)

if r.object('encoding', 'zsetkey') != None:
    print('Key为【zsetkey】的字节编码为【%s】' % r.object('encoding', 'zsetkey').decode('utf-8'))
Key为【zsetkey】的字节编码为【ziplist】
Key为【zsetkey】的字节编码为【skiplist】

3.当有序集合中有任何一个元素大于64个字节时,内部编码为skiplist。

import redis

r = redis.Redis(host='127.0.0.1', port=6379)

if r.object('encoding', 'zsetkey') != None:
    print('Key为【zsetkey】的字节编码为【%s】' % r.object('encoding', 'zsetkey').decode('utf-8'))

value = ''
for i in range(1, 600):
    value += str(i)

r.zadd('zsetkey',value,1)

if r.object('encoding', 'zsetkey') != None:
    print('Key为【zsetkey】的字节编码为【%s】' % r.object('encoding', 'zsetkey').decode('utf-8'))
Key为【zsetkey】的字节编码为【skiplist】

img


上述内容就是Redis中有序集合的内容,如有不正确的地方,欢迎留言,谢谢。


原文链接:http://jilinwula.com/article/...