今天我们继续学习Redis中的数据类型,今天我们学习有序集合类型,也是Redis中的5大数据类型中的最后一个。看名字,我们就知道,有序集合也是一种集合,并且这个集合还是有序的。那有序集合和列表有什么不同呢?因为列表也是有序的。 它们到底有什么不同呢?有序集合的有序和列表的有序是不同的。列表中的有序指的的是插入元素的顺序,和查询元素的顺序相同。而有序集合中的有序指的是它会为每个元素设置一个分数(score),而查询时可以通过分数计算元素的排名,然后在返回结果。因为有序集合也是集合类型,所以有序集合中也是不插入重复元素的,但在有序集合中分数则是可以重复,那如果在有序集合中有多个元素的分数是相同的,那么这些重复的元素的排名是怎么计算的呢?在下面的内容中我们在做详细说明。下面我们看一下列表、集合、有序集合的它们3个数据类型之间的区别。
数据结构 | 是否允许重复元素 | 是否有序 | 有序实现方式 | 应用场景 |
---|---|---|---|---|
列表 | 是 | 是 | 索引下标 | 时间轴、消息队列 |
集合 | 否 | 否 | 无 | 标签、社交 |
有序集合 | 否 | 是 | 分数 | 排行榜、社交 |
下面我们重点了解一下Redis中有序集合的相关命令。
命令
一、集合内
1.添加元素
zadd key [NX|XX] [CH] [INCR] score member [score member ...]
zadd命令也是有返回值的,返回值就是当前zadd命令成功添加元素的个数。除此之外,zadd命令还有很多其它的选填参数。下面我们详细了解一下:
- nx: 元素必须不存在时,才可以设置成功。
- xx: 元素必须存在时,才可以设置成功。
- ch:
- incr: 对score做增加。
备注:由于有序集合相比集合提供了排序字段,正是因为如此也付出了相应的代价,,zadd的时间复杂度为O(log(n)),sadd的时间复杂度为O(1)。
2.计算成员个数
zcard key
3.计算某个成员的分数
zscore key member
在使用zscore命令时,如果key不存在,或者元素不存在时,该命令返回的都是(nil)。
4.计算成员的排名
zrank key member
zrevrank key member
zrank命令是从分数低到高排名,而zrevrank命令则恰恰相反,从高到低排名。有一点要特别注意zrank和zrevrank命令与zscore命令不同的前者是通过分数计算出最后的排名,而后者则是直接返回当前元素的分数。
5.删除元素
zrem key member [member ...]
返回的结果为成功删除元素的个数,因为zrem命令是支持批量删除的。
6.增加元素分数
zincrby key increment member
虽然zincrby命令是增加元素分数的,但我们也可以指定负数,这样当前元素的分数,则会相减。
7.返回指定排名范围的元素
zrange key start stop [WITHSCORES]
zrevrange key start stop [WITHSCORES]
zrange命令是通过分数从低到高返回数据,而zrevrange命令是通过分数从高到低返回数据。如果执行命令时如果添加了WITHSCORES可选参数,而返回数据时会返回当前元素的分数。
8.返回指定分数范围的元素
zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]
min和max参数同时还支持开区间(小括号)和闭区间(中括号),同时我们还可以用-inf和+inf参数代表无限小和无限大。
备注:闭区间的没有验证成功,执行时会报错,这个有待验证。
9.返回指定分数范围元素个数
zcount key min max
10.删除指定指定排名内的升序元素
zremrangebyrank key start stop
11.删除指定分数范围元素
zremrangebyscore key min max
二、集合间操作
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。
下面我们将权重设置为0.5,这样当计算交集后,有序集合中的元素分数将都会减半,并且使用max参数汇总。
2.并集
zunionstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]
zunionstore命令的相关参数和zinterstore命令相同。
时间复杂度
下面我们了解一下上述命令的时间复杂度。
命令 | 时间复杂度 |
---|---|
zadd key [NX/XX] [CH] [INCR] score member [score member ...] | O(k*log(n)),k是添加元素的个数,n是当前有序集合元素个数 |
zcard key | O(1) |
zscore key member | O(1) |
zrank key member | O(log(n)),n是当前有序集合元素个数 |
zrevrank key member | O(log(n)),n是当前有序集合元素个数 |
zrem key member [member ...] | O(k*log(n)),k是删除元素的个数,n是当前有序集合元素个数 |
zincrby key increment member | O(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 max | O(log(n)),n是当前有序集合元素个数 |
zremrangebyrank key start stop | O(log(n) + k),k是要删除的元素个数,n是当前有序集合个数 |
zremrangebyscore key min max | O(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:
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】
上述内容就是Redis中有序集合的内容,如有不正确的地方,欢迎留言,谢谢。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。