Redis数据结构-Zset有序集合
1.Zset有序集合
有序集合保留了集合不能有重复成员的特点,但与集合不同的是,有序集合中的每个元素都有一个唯一的浮点类型的分数与之关联,这使得有序元素中的元素是可以维护有序行的,但这个有序不是用下表作为排序依据而是用这个分数(进行排序时,就是按照分数大小来进行按照升/降序排序)
注:有序集合中的元素是不能重复的,但是分数运行重复。当分数相同时,会安好元素自身的字符串的字典序来排序
2.常用命令
2.1 ZADD
添加或者更新指定的元素以及关联的分数到zset中,分数应该符合double类型,+inf/-inf作为正负极线也是合法的
ZADD的相关选项:
XX:仅仅用于更新已经存在的元素,不会添加新的元素
NX:仅用于添加新元素,不会更新已经存在的元素
CH:默认情况下,ZADD返回本次添加元素的个数,但是指定这个选项后,就会包干本次更新的元素个数(添加的元素个数+更新的元素个数)
INCR:此时命令类似于ZINCRBY的效果,将元素的分数加上指定的分数,此时只能制定一个元素和分数
LT:小于
GT:大于
score:可以填写小数
语法:ZADD key [ NX | XX ] [ GT | LT ] [ CH ] [ INCR ] score member [score member ...]
时间复杂度:O(logN),由于Zset是有序结构的,要求新增的元素要放到合适的位置上
返回值:本次添加成功的元素个数
示例:
由此可见,Zset内部默认是按照升序方式排列的
2.2 ZCARD
获取一个zset的技术(cardinality),即zset中元素的个数
语法:ZCARD key
时间复杂度:O(1)
返回值:zset内的元素个数
示例:
2.3 ZCOUNT
返回分数在min和max之间的元素个数,默认情况下,min和max都是包含的,可以通过(排除(此处的min,max表示的分数)
语法:ZCOUNT key min max
时间复杂度:O(logN)
返回值:满足条件的元素列数个数
示例:
2.4 ZRANGE
返回指定区间里的元素,分数按照升序。带上WITHSCORES可以吧分数也返回
语法:ZRANGE key start stop
此处的[start,stop]为下标构成的区间,从0开始支持负数
时间复杂度:O(logN+M)
返回值:区间内的元素列表
示例:
2.5 ZREVRANGE
返回指定区间里的元素,分数按照降序。带上withscores可以把分数也返回
语法:ZREVRANGE key start stop [WITHSCORES]
时间复杂度:O(logN+M)
返回值:区间内的元素列表
2.6 ZRANGEBYSCORE
返回分数在min和max之间的元素,默认情况下,min和max都是包含的,可以通过(排除
语法:ZRANGEBYSCORE key min max [WITHSCORES]
时间复杂度:O(logN+M)
返回值:区间内的元素列表
示例:
2.7 ZPOPMAX
删除并返回分数最高的count个元素
语法:ZPOPMAX key [count]
时间复杂度:O(log(N)*M)
返回值:分数和元素列表
示例:
2.8 ZPOPMIN
删除并返回分数最低的count个元素
语法:ZPOPMIN key [count]
时间复杂度:O(log(N)*M)
返回值:分数和元素列表
示例:
2.9 ZRANK
返回指定元素的排名,次序
语法:ZRANK key member
时间复杂度:O(logN)
返回值:排名(当member不存在时,返回nil)
示例:
2.10 ZREVRANK
返回指定元素的排名,降序
语法:ZREVRANK key member
时间复杂度:O(logN)
返回值:排名(member不存在时,返回nil)
示例:
2.11 ZSCORE
返回指定元素的分数
语法:ZSCORE key member
时间复杂度:O(1)(此处相当于Redis针对查询操作做了优化处理,付出了额外的空间代价,优化到O(1))
返回值:分数
示例:
2.12 ZREM
删除指定元素
语法:ZREM key member [member ...]
时间复杂度:O(M*logN)
返回值:本次操作删除的元素个数
示例:
2.13 ZREMRANGEBYRANK
按照排序,升序删除指定范围的元素,左闭右闭
语法:ZREMRANGEBYRANK key start stop
时间复杂度:O(logN+M)
返回值:本次操作删除的元素个数
示例:
2.14 ZREMRANGEBYSCORE
按照分数删除指定返回的元素,左闭右闭
语法:ZREMRANGEBYSCORE key min max
时间复杂度:O(logN+M)
返回值:本次操作删除的元素个数
示例:
2.15 ZINCRBY
为指定的元素的关联分数添加指定的分数值
语法:ZINCRBY key increment member(increment支持负数和小数)
时间复杂度:O(logN)
返回值:增加后元素的分数
示例:
阻塞版本指令
2.16 BZPOPMAX
ZPOPMAX的阻塞版本,当有序集合中为空时阻塞,阻塞到队列中插入元素
语法:BZPOPMAX key [key ...] timeout
时间复杂度:O(logN)
返回值:元素列表
2.17 BZPOPMIN
ZPOPMIN的组设版本,与BZPOPMAX用法一致
语法:BZPOPMIN key [key ...] timeout
时间复杂度:O(logN)
返回值:元素列表
集合间操作指令
2.18 ZINTERSTORE
求出给定有序集合中元素的交集并保存到目标有序集合中,在合并过程中以元素为单位进行合并,元素对应的分数按照不同的聚合方式和权重得到新的分数
语法:ZINTERSCORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE <SUM | MIN | MAX>]
destination:表示保存到的目标有序结合
numkeys:整数,描述了后续有多少个key参与交集运算(为了避免选项和keys混淆)
AGGREGATE:总数,合计。默认情况下求和
WEIGHTS:权重,分数会乘以对应的权重
时间复杂度:O(N*K)+O(M*logM),N是输入的有序集合中,最小的有序集合的元素个数,K是输入了几个有序集合,M是最终结果的有序集合的元素个数
返回值:目标集合中的元素个数
示例:
2.19 ZUNIONSTORE
求出给定有序集合中元素的并集并保存进目标有序集合中,在合并过程中以元素为单位进行合并,元素对应的分数按照不同的聚合方式和权重重新得到新的分数
语法:ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE <SUM | MIN | MAX>]
时间复杂度:O(N)+O(M*logM)(N是输入有序集合中总的元素个数,M是最终结果的有序结合的元素个数)
返回值:目标集合中元素的个数
示例:
3.内部编码
3.1 ziplist
ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplist-entries配置(默认128个),同时每个元素的值都小于zset-max-ziplist-value配置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存的使用
3.2 skiplist
skiplist(跳表):当ziplist条件不满足时,有序集合会使用skiplist作为内部实现,因为此时ziplist的操作效率会下降。(此处不详讲)
4.Zset的经典使用场景
4.1 排行榜系统
最常见的应用场景:微博热搜,游戏天梯排行榜,成绩排行
关键要点:用来排行的分数是实时变化的,虽然是实时变化的,也能够高效的更新排行
使用zset完成上述操作,就会非常简单。比如游戏天梯排行,只需要吧玩家信息和对应的分数给放到有序集合中即可,自动形成了一个排行榜,随时可以按照排行(下标)按照分数,进行范围查找随者分数发生变化,也可以比较方便的,zincrby修改分数,排行顺序也能自动调整(logN)
对于游戏排行榜,前后顺序非常容易确定,但是有些排行榜就要复杂一些,例如微博热搜(综合数值:浏览量,点赞量,转发量,评论量;具体有多少个维度,每个维度权重怎么分配以及怎么设定最优)根据每个维度,计算得到综合得分=热度,此时就可以借助zinterstore/zunionstor按照加权方式处理,此时就可以把上述维度的数值都放入一个有序集合中,member是微博id,score就是各自维度的数值,通过zinterstore或zunionstore把上述有序集合约定好的权重,进行集合间运算,得到的结果集合的分数就是热度
5.列表,集合,有序集合之间的异同点
数据结构 | 是否允许重复元素 | 是否有序 | 有序依据 | 应用场景 |
列表 | 是 | 是 | 索引下标 | 时间轴,消息队列 |
集合 | 否 | 否 | 标签,社交 | |
有序集合 | 否 | 是 | 分数 | 排行系统,社交等 |