详尽Redis有序集合指令
Redis基础数据类型的最后一种常用的数据类型,有序集合能够在日常开发中给我们提供更多的想象空间。
在这里,一些示例直接采用了try.redis.io中的例子,文中有标注。
有序集合类型
有序集合在集合类型的基础上,对每个元素添加了一个分数,除了具备集合类型的基础功能外,如添加、删除、查找、判断元素是否存在等,还能够根据分数进行一些相关操作,查询分值区间的元素等。
有序集合和列表类型也有一定的相似度,比如有序集合和列表都是有序的,并且可以提供获取区域元素,在一定程度上可以实现分页的功能。但两者也有本质的区别,列表主要是双向链表数据结构,查询两端元素性能比较高,随着元素的增多,越靠近中间的元素查询性能越低,列表对于实现小规模数据的存储,比如个人收藏等,或者排行榜、最新日志等有较好的体现;有序集合采用Hash、跳跃表等实现,因此对于大数据量,中间查询也有较快的表现;列表因为采用双向链表实现,因此调整顺序比较麻烦,有序集合在调整顺序时,只需要调整分数即可。
添加元素
ZADD key score member [score member ...]
ZADD指令用于向有序集合添加一组或多组元素+元素分数,当元素已存在时,将更新分数。该命令将当前插入成功的元素个数返回,不包含已存在的元素。
将名画家的作品按创作年份插入到有序集合(try.redis.io)。
ZADD a 1965 "girl with a pearl earring" 1895 "The Last Supper" 1987 "The Death of Marat"
# (integer) 3
此时,要调整The Death Of Marat
的创作日期,同时需要新增一副作品
ZADD a 1978 "The Death of Marat" 1901 "Marilyn Monroe"
# (integer) 1
由于The Death Of Marat
作品已存在,因此只更新了分数1978,新增了一副作品,指令返回值为新增作品数1
。
得分除了整数外,也支持浮点数。
可以使用
+inf
和-inf
表示分数,分别代表正无穷大和负无穷大
ZADD a 1978.8 "The Death of Marat"
# (integer) 0
ZADD +inf "The dog" -inf "The cat"
# (integer) 2
ZRANGE a 0 -1 # 升序排列,-inf最小,+inf最大
# 1) "The cat"
# 2) "The Last Supper"
# 3) "Marilyn Monroe"
# 4) "girl with a pearl earring"
# 5) "The Death of Marat"
# 6) "The dog"
获取元素分数
ZSCORE key member
ZSCORE指令用于获取元素的得分,该指令每次只能返回一个元素的得分。如果指定的key或者member不存在时,该指令返回nil
。
ZSCORE a "The cat"
# "-inf"
ZSCORE a "The Death of Marat"
# "1978.8"
ZSCORE a b
# (nil)
ZSCORE ab b
# (nil)
按升序获取指定范围的元素列表
ZRANGE key start stop [WITHSCORES]
ZRANGE指令用于按SCORE升序返回start到stop之间的元素,包括start和stop索引位置的元素。该指令与来列表的LRANGE指令类似,只不过一个按插入排序,一个按SCORE升序,其他的STOP、START取值及使用方式一致。
ZRANGE a 0 -1 # 按升序返回全部元素
# 1) "The cat"
# 2) "The Last Supper"
# 3) "Marilyn Monroe"
# 4) "girl with a pearl earring"
# 5) "The Death of Marat"
# 6) "The dog"
# 从右向左获取元素
ZRANGE a -3 -1
# 1) "girl with a pearl earring"
# 2) "The Death of Marat"
# 3) "The dog"
STOP、START取值和LRANGE的方式一致,START必须小于等于STOP,否则返回空。
ZRANGE指令有一个可选参数,在指令末尾加上WITHSCORES
,这样可以将元素得分一起返回,其格式为:元素,得分,元素,得分...
,每个元素后紧跟着该元素的得分。
ZRANGE a 0 3 WITHSCORES
# 1) "The cat"
# 2) "-inf"
# 3) "The Last Supper"
# 4) "1895"
# 5) "Marilyn Monroe"
# 6) "1901"
# 7) "girl with a pearl earring"
# 8) "1965"
前面提到,ZRANGE在大数据量下,取中间的数据性能优于列表类型,其查找元素时间复杂度为O(logn + m),n为有序集合的基数,m为要返回的元素个数
。当部分元素得分相同的情况下,将按照字典顺序进行排列(数字<大写字母<小写字母,每种类型内部也是升序
)。
ZRANGE a 1 2 WITHSCORES
# 1) "ABC"
# 2) "1895"
# 3) "The Last Supper"
# 4) "1895"
按降序获取指定范围的元素列表
ZREVRANGE key start stop [WITHSCORES]
ZREVRANGE指令的使用方式同ZRANGE完全一致,唯一区别就是按降序返回数据。
ZREVRANGE a 0 -1 WITHSCORES
# 1) "The dog"
# 2) "inf"
# 3) "The Death of Marat"
# 4) "1978.8"
# 5) "girl with a pearl earring"
# 6) "1965"
# 7) "Marilyn Monroe"
# 8) "1901"
# 9) "The Last Supper"
# 10) "1895"
# 11) "ABC"
# 12) "1895"
# 13) "The cat"
# 14) "-inf"
按指定得分范围返回元素列表,从小到大排列
ZRANGEBYSCORE key min max [WITHSCORE] [LIMIT offset count]
ZRANGEBYSCORE指令用于从有序列表中,返回min<=分数<=max
的所有元素,并按从小到大排列。min和max的取值同以往的分值参数一致,支持+inf(正无穷)、-inf(负无穷)。同时为了支持大于和小于的操作,提供一个(
英文小括号,用于加在数字前面,表示大于(min
或者小于(max
的数值,此时不包括max和min的分值。
WITHSCORE可选参数,同之前的命令一致,当携带此参数时,将返回得分,仍然元素,分数,元素,分数...
格式返回。
LIMIT offset count
可选指令参数,用于限制返回的数量,同MySql的Limit
指令类似,offset
表示从那个位置开始返回数据,count
用于表示返回多少个元素。
ZRANGE a 0 -1 WITHSCORES # 有序列表存储了学生的生日
# 1) "lucy"
# 2) "1979"
# 3) "paul"
# 4) "1982"
# 5) "ray"
# 6) "1983"
# 7) "hh"
# 8) "1988"
# 9) "viciky"
#10) "1997"
#11) "xx"
#12) "2011"
# 获取80后的学生信息
ZRANGEBYSCORE a 1980 1989
# 1) "paul"
# 2) "ray"
# 3) "hh"
在上面的指令上,如果希望不包含1980年的学生,则可以使用(
指令加在min
上。
ZADD a 1990 jack
ZRANGEBYSCORE a 1980 (1990 WITHSCORES
# 1) "paul"
# 2) "1982"
# 3) "ray"
# 4) "1983"
# 5) "hh"
# 6) "1988"
当希望返回所有的80年以后出生的学生,但不知道最大的出生年龄(实际可以设置当前年份)时,可以使用+inf
参数。
ZRANGEBYSCORE a 1980 +inf
# 1) "paul"
# 2) "ray"
# 3) "hh"
# 4) "jack"
# 5) "viciky"
# 6) "xx"
返回80后,从第二个学生开始,并返回三个学生,可以借助LIMIT参数。
ZRANGEBYSCORE a 1980 +inf LIMIT 1 3
# 1) "ray"
# 2) "hh"
# 3) "jack"
按指令得分返回从大到小返回元素列表
ZREVRANGEBYSCORE key max min [WITHSCORE] [LIMIT offset count]
ZREVRANGEBYSCORE指令同ZRANGEBYSCORE指令基本一致,只是返回结果是按照分数从大到小排列,同时分数的限制条件为 max min
,先大后小,也是和ZRANGEBYSCORE指令相反,其他都一致。
ZREVRANGEBYSCORE a +inf 1990 WITHSCORES
# 1) "xx"
# 2) "2011"
# 3) "viciky"
# 4) "1997"
# 5) "jack"
# 6) "1990"
上述指令返回90后学生信息,并按年龄从小到大排列。
给某元素增加分数
ZINCRBY key increment member
ZINCRBY指令用于对有序列表中某元素增加一个分数,即原始分数+增加的分数,并将该分数返回。如上述学生列表中,jack
的出生年份录入错误,实际生日为1992,则可以使用增加分数指令。
ZINCRBY a 2 jack
# "1992"
ZINCRBY a -2 xx
# "2009"
当要增加分数的元素不存在时,相当于ZADD命令。如
ZINCRBY a -2 x # x学生不存在
# "-2"
获取集合元素数量
ZCARD key
ZCARD可以返回有序集合的元素数量。
ZCARD a
# "8"
获取指定分数范围内的元素数量
ZCOUNT key min max
ZCOUNT指令可以获取min、max之间(包含关系同ZRANGEBYSCORE一致,同时也支持-inf,+inf参数)的元素数量。
ZCOUNT a (1982 1990
# "2"
删除一个或多个元素
ZREM key member [member ...]
ZREM指令用于从有序列表中删除指定的一个或多个元素,并返回成功删除的元素数量。如果元素不存在,则返回数量不包含该元素。
ZCARD a
# (integer) 8
ZREM a y x # y不存在,x存在
# (integer) 1
ZCARD a
# (integer) 7
按照排序范围删除元素
ZREMRANGEBYRANK key start stop
有序集合已经排序,ZREMRANGEBYRANK指令,将按照元素的从小到大书序,删除指定的start、stop范围内的所有元素,包括start、stop位置的元素,并返回删除的元素数量。该指令类似于列表的LTRIM指令,列表按照插入也是有序的。
# 删除了年龄最大的前3个学生。
ZREMRANGEBYRANK a 0 2
# (integer) 3
ZRANGE a 0 -1 # 剩余4个学生
# 1) "hh"
# 2) "jack"
# 3) "viciky"
# 4) "xx"
# 删除剩余学生中年龄最小的两个人
ZREMRANGEBYRANK a -2 -1
# (integer) 2
ZRANGE a 0 -1 # 剩余2个学生
# 1) "hh"
# 2) "jack"
当指令指定范围超过有序列表的元素数量时,将删除全部元素,并返回成功删除的个数
按照分数范围删除元素
ZREMRANGEBYSCORE key min max
ZREMRANGEBYSCORE命令将删除指定分数范围内的所有元素,查找方式和min、max的取值方式同ZRANGEBYSCORE指令的参数设置方式完全相同,当删除完成时,返回删除的元素数量。
学生出生日期有序列表中还剩余两个学生,使用生日范围删除
ZRANGE a 0 -1
# 1) "lucy"
# 2) "jack"
ZREMRANGEBYSCORE a 1990 (2000
# (integer) 1
获取元素的排名
ZRANK key member
ZRANK指令按照有序列表从小到大顺序获取元素的排名,由于数据的索引从0开始,因此返回的实际是索引值。
ZADD a 1987 ray 1990 hh
ZRANGE a 0 -1
# 1) "ray"
# 2) "hh"
# 3) "jack"
ZRANK a hh
# (integer) 1
获取元素降序排名
ZREVRANK key member
ZREVRANK同ZRANK基本一致,只不过是降序排名。
ZREVRANK a hh
# (integer) 1
ZREVRANK a jack
# (integer) 0
有序集合的交集
ZINTERSTORE destination numKeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
ZINTERSTORE指令用于计算给定的一个或多个集合的交集,并将结果存储到destination集合中。其中key的数量必须与numKeys一致,如numKeys=2
,则其后需要跟两个集合的key。
交集计算后的元素分数,由AGGREGATE参数确定。其支持三种方式:
- SUM,交集计算后元素分数为多个集合中该元素的分数之和,SUM为默认值。
- MIN,交集计算后元素的分数为多个集合中该元素的最小分数。
- MAX,交集计算后元素的分数为多个集合中该元素的最大分数。
创建一个学生的多科成绩表:
# 集合a为语文成绩,集合b为数学成绩
ZADD a 98 ray 89 hh 78 xx 80 jack
# (integer) 4
ZADD b 87 ray 100 hh 78 xx 90 tom
# (integer) 4
使用默认进行交集计算学生的两科总成绩,此时传递AGGREGATE参数为SUM或者不传递AGGREGATE参数,效果一致。
ZINTERSTORE c 2 a b # jack没有参加数学,tom没有参加语文,因此总成绩只有三人,并将结果存储到c集合中
# (integer) 3
ZRANG c 0 -1 WITHSCORES # 此时交集为各科成绩之和
# 1) "xx"
# 2) "156"
# 3) "ray"
# 4) "185"
# 5) "hh"
# 6) "189"
采用AGGREGATE参数,取参与每科考试的学生,成绩较差的一科,用于分析学生考试差的原因。
ZINTERSTORE d 2 a b AGGREGATE MIN # 结果存储到d集合,有三人数据
# (integer) 3
ZRANGE d 0 -1 WITHSCORES # 此时交集为各科最差成绩
# 1) "xx"
# 2) "78"
# 3) "ray"
# 4) "87"
# 5) "hh"
# 6) "89"
获取参与考试的学生考的最好学科,采用MAX参数。
ZINTERSTORE e 2 a b AGGREGATE MAX # 结果存储到e集合
# (integer) 3
ZRANGE e 0 -1 WITHSCORES # 此时交集为各科最好成绩
# 1) "xx"
# 2) "78"
# 3) "ray"
# 4) "98"
# 5) "hh"
# 6) "100"
交集参数中还有一个参数WEIGHTS
,用于指定每个集合中的得分权重,计算时,每个集合中的元素分数会乘上该集合的权重,集合的权重与集合key的顺序一致。如果指定WEIGHTS参数时,后面跟谁的权重参数必须和key的个数对应,否则将会报指令格式不合法的异常。
在计算两科总成绩时,由于语文成绩较难,因此需要加上一个1.2倍的系数,作为总成绩。
ZINTERSTORE f 2 a b WEIGHTS 1.2 1
# (integer) 3
ZRANGE f 0 -1 WITHSCORES
# 1) "xx"
# 2) "171.59999999999999" # = 78 * 1.2 + 78
# 3) "ray"
# 4) "204.59999999999999"
# 5) "hh"
# 6) "206.80000000000001"
有序集合的并集
ZUNIONSTORE destination numKeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
ZUNIONSTORE指令用于计算集合的并集,并将结果存储到destination中。中key的数量必须与numKeys一致,如numKeys=2
,则其后需要跟两个集合的key。
除了执行并集外,其余参数及指令格式均与有序集合的交集一致。
采用并集计算参加考试的所有学生总成绩,此时即使缺考也会被计算。
ZUNIONSTORE g 2 a b
# (integer) 5
ZRANGE g 0 -1 WITHSCORES
# 1) "jack"
# 2) "80"
# 3) "tom"
# 4) "90"
# 5) "xx"
# 6) "156"
# 7) "ray"
# 8) "185"
# 9) "hh"
#10) "189"