有序集合zset保留了set集合不能有重复成员的特点,但与set集合不同的是,zset的每个member都有一个唯一的浮点数类型的分数score与之关联。依据每个member的score进行排序。结构如下图所示:
有序集合中的元素是不能重复的,但分数允许重复
添加或更新指定元素以及关联的分数到zset中,分数是double类型
语法:zadd key [nx|xx] [gt|lt] [ch] [incr] score member [score member...]
我使用的版本是redis5,在redis5中不支持 gt/lt操作
时间复杂度:O(logN)
由于zset是有序结构,要求新增的元素,要放到合适的位置上即找位置的过程,zset内部使用跳表的数据结构,可以达到O(logN)
:::danger
zset中的member和score不是键值对的关系,而是“pair”的关系,可以通过member找到score,也可以通过score找到member
:::
查看zset中元素详情
语法:zrange key start stop
时间复杂度:O(logN+M)
首先根据下标找到边界值,找元素O(logN),从start位置开始遍历到stop位置,O(m)
redis内部存储数据是按照二进制存储,所以redis服务器不负责“字符编码”,因此需要redis客服端支持
zset内部默认是按照升序的方式来排列的
返回指定区间元素,分数按降序排列,带上withscores
可以将分数加上
语法:zrevrange key start stop [withscores]
时间复杂度:O(logN+M)
查看一共有几个元素
语法:zcard key
时间复杂度:O(1)
根据给定分数区间查找有几个元素
语法:zcount key min max
闭区间
时间复杂度:O(logN)
先根据min找到对应元素,再根据max找到对应元素,而查找元素的时间复杂度是O(logN),
由于zset内部记录每个元素的“次序”,可以直接把max对应元素的次序减去min对应元素的次序即可,不需要进行遍历。
使用(
表示开区间
min和max可以写成浮点数,浮点数中inf表示无穷大,-inf表示负无穷大
返回分数在min和max之间的元素
语法:zrangebyscore key min max [withscores]
时间复杂度:O(logN+M)
在redis6以后该命令被废弃,将功能合并到zrange中
删除并返回分数最高的count个元素,类似topk问题
语法:zpopmax key [count]
时间复杂度:O(logN*M)
删除M个元素,查找一个元素时间复杂度为O(logN)
zpopmax的阻塞版本
语法:bzpopmax key [key...] timeout
时间复杂度:O(logN)
返回值:返回元素列表
删除并返回分数最低的count个元素
语法:zpopmin key [count]
时间复杂度:O(logN*M)
返回值:返回分数和元素列表
zpopmin的阻塞版本
语法:bzpopmin key [key...] timeout
时间复杂度:O(logN)
返回值:返回元素列表
返回指定元素的次序,按升序排
语法:zrank key member
时间复杂度:O(logN)
返回值:返回对应次序/下标
zrank的降序版本
语法:zrevrank key member
时间复杂度:O(logN)
返回值:返回对应元素次序/下标
返回指定元素的分数
语法:zscore key member
时间复杂度:O(1)
在前面的命令中根据member找元素的时间复杂度都是O(logN),但是这里redis牺牲空间,换取时间做了特定的优化
删除指定元素
语法:zrem key member [member...]
返回值:分数
根据下标表示的范围进行删除,闭区间
语法:zremrangebyrank key start stop
时间复杂度:O(logN+M)
查找位置只需一次即可,然后删除M个元素
删除对应的分数范围内的元素
语法:zremrangebyscore key min max
时间复杂度:O(logN+M)
返回值:返回删除个数
修改元素的分数,并保持整个有序集合仍然是升序的
语法:zincrby key increment member
时间复杂度:O(logN)
返回值:增加后元素的分数
将给定有序集合中元素的交集并保持到目标有序集合中
语法:zinterstore destination numkeys key [key...] [weights weight [weight ...]] [aggregate
时间复杂度:O(NK)+O(Mlog(M)) -> O(M*logM)
N:输入若干个有序集合中,元素最少的个数
K:有几个有序集合
M:最终有序集合的元素个数
k一般不会太大,可以看作1,N和M接近,同一个数量级。
所以O(NK)+O(Mlog(M))->O(M)+O(Mlog(M))-> O(MlogM)
c:300.5+102=35
a:100.5+202=45
将给定有序集合中元素的并集并保存进目标有序集合中
语法:zunionstore destination numkeys key [key...] [weights weight [weight...]][aggregate
用法和zinterstore
一致
如果有序集合中的元素个数较少,或者各个元素体积较小使用ziplist
来存储,如果当前元素个数比较多,或者各个元素体积非常大使用skiplist
存储
最关键的应用场景就是排行榜系统,例如微博热搜,游戏天梯排行,成绩排行
使用zset将玩家信息和对应分数存入即可,就会自动生成一个排行榜,可以使用
zrevrange
取出前几名玩家。当分数发生改变,可以使用zincrby
修改分数,排行顺序能自动调整。
微博热搜需要考虑到浏览量,点赞量,转发量,评论量等,需要使用权重
weights
,此时可以使用zinterstore/zunionstore
按照加权的方式处理。可以将每个维度的数值放到有序集合中,member
就是微博id,score
就是各个维度的数值。通过zinterstore/zunionstore
把上述有序集合按照约定的权重,进行集合间运算即可。