各位小伙伴们,我们上次讲过Redis中Set的相关操作,以及适用的一些场景,例如抽奖等。今天要讲的sorted_set,其实也是set的一种,也具有set去重的特点。和set不同的是,sorted_set是有序的,从字面意思也可以看得出来。
那sorted_set是怎么排序的,根据什么规则排序呢?往下看。
我们先来看下sored_set提供了哪些命令。
127.0.0.1:6379> help @sorted_set
BZPOPMAX key [key ...] timeout
summary: Remove and return the member with the highest score from one or more sorted sets, or block until one is available
since: 5.0.0
BZPOPMIN key [key ...] timeout
summary: Remove and return the member with the lowest score from one or more sorted sets, or block until one is available
since: 5.0.0
ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
summary: Add one or more members to a sorted set, or update its score if it already exists
since: 1.2.0
ZCARD key
summary: Get the number of members in a sorted set
since: 1.2.0
ZCOUNT key min max
summary: Count the members in a sorted set with scores within the given values
since: 2.0.0
ZINCRBY key increment member
summary: Increment the score of a member in a sorted set
since: 1.2.0
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]
summary: Intersect multiple sorted sets and store the resulting sorted set in a new key
since: 2.0.0
ZLEXCOUNT key min max
summary: Count the number of members in a sorted set between a given lexicographical range
since: 2.8.9
ZPOPMAX key [count]
summary: Remove and return members with the highest scores in a sorted set
since: 5.0.0
ZPOPMIN key [count]
summary: Remove and return members with the lowest scores in a sorted set
since: 5.0.0
ZRANGE key start stop [WITHSCORES]
summary: Return a range of members in a sorted set, by index
since: 1.2.0
ZRANGEBYLEX key min max [LIMIT offset count]
summary: Return a range of members in a sorted set, by lexicographical range
since: 2.8.9
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
summary: Return a range of members in a sorted set, by score
since: 1.0.5
ZRANK key member
summary: Determine the index of a member in a sorted set
since: 2.0.0
ZREM key member [member ...]
summary: Remove one or more members from a sorted set
since: 1.2.0
ZREMRANGEBYLEX key min max
summary: Remove all members in a sorted set between the given lexicographical range
since: 2.8.9
ZREMRANGEBYRANK key start stop
summary: Remove all members in a sorted set within the given indexes
since: 2.0.0
ZREMRANGEBYSCORE key min max
summary: Remove all members in a sorted set within the given scores
since: 1.2.0
ZREVRANGE key start stop [WITHSCORES]
summary: Return a range of members in a sorted set, by index, with scores ordered from high to low
since: 1.2.0
ZREVRANGEBYLEX key max min [LIMIT offset count]
summary: Return a range of members in a sorted set, by lexicographical range, ordered from higher to lower strings.
since: 2.8.9
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
summary: Return a range of members in a sorted set, by score, with scores ordered from high to low
since: 2.2.0
ZREVRANK key member
summary: Determine the index of a member in a sorted set, with scores ordered from high to low
since: 2.0.0
ZSCAN key cursor [MATCH pattern] [COUNT count]
summary: Incrementally iterate sorted sets elements and associated scores
since: 2.8.0
ZSCORE key member
summary: Get the score associated with the given member in a sorted set
since: 1.2.0
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]
summary: Add multiple sorted sets and store the resulting sorted set in a new key
since: 2.0.0
可以看到,sorted_set 中的命令大多以 Z 开头,因为set占用 S 了嘛。还有某些命令中有REV关键字,代表Reverse,反向的意思。
另外,还有两个命令以B开头,这里B代表Blocked,即阻塞的,上述帮助文档里也有说明(block until one is available)。
命令看起来挺多,我们大致做个分类。
sorted_set的基本用法
下面,我们来看下常用命令的具体用法。
一、基本操作
从ZADD命令可以看出,向key中添加元素时,有个参数score。没错,sorted_set 对每个元素都设定了分值,并在add时,根据这个分值进行升序排序。
ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
summary: Add one or more members to a sorted set, or update its score if it already exists
since: 1.2.0
/* 将笔试成绩(tom-100,jack-60,tony-80)放入名为 writtenTestScore 的
有序集合里,返回元素个数3*/
127.0.0.1:6379> zadd writtenTestScore 100 tom 60 jack 80 tony
(integer) 3
/面试成绩*/
127.0.0.1:6379> zadd interviewScore 70 tom 80 jack 60 tony
(integer) 3
/*zrange列出所有元素*/
127.0.0.1:6379> zrange writtenTestScore 0 -1
1) "jack"
2) "tony"
3) "tom"
/*zrange列出所有元素(笔试成绩),带分值*/
127.0.0.1:6379> zrange writtenTestScore 0 -1 withscores
1) "jack"
2) "60"
3) "tony"
4) "80"
5) "tom"
6) "100"
/*列出面试成绩*/
127.0.0.1:6379> zrange interviewScore 0 -1 withscores
1) "tony"
2) "60"
3) "tom"
4) "70"
5) "jack"
6) "80"
下面是删除元素的一些操作,包括
ZPOPMAX/BZPOPMAX,ZPOPMIN/BZPOPMIN、ZREM等。
127.0.0.1:6379> ZADD testREM 1 a 2 b 3 c 4 d
(integer) 4
127.0.0.1:6379> zpopmax testREM
1) "d"
2) "4"
127.0.0.1:6379> zrem testREM a
(integer) 1
/*删除10个最大的元素并返回*/
127.0.0.1:6379> zpopmax testREM 10
1) "c"
2) "3"
3) "b"
4) "2"
/*元素为空,阻塞1s后,返回空*/
127.0.0.1:6379> BZPOPMAX testREM 1
(nil)
(1.01s
/*取某个元素的分值*/
127.0.0.1:6379> ZSCORE interviewScore tony
"60"
二、统计类操作
统计类操作大致包括两类,一类是取元素的个数或排名,一类是取元素的列表。
/*列出80-100分数段的元素个数*/
127.0.0.1:6379> zcount writtenTestScore 80 100
(integer) 2
/*返回集合中元素个数*/
127.0.0.1:6379> zcard writtenTestScore
(integer) 3
/*取jack的排名*/
127.0.0.1:6379> ZRANK writtenTestScore jack
(integer) 0
/*取出80-100分数段的元素*/
127.0.0.1:6379> ZRANGEBYSCORE writtenTestScore 80 100 withscores
1) "tony"
2) "80"
3) "tom"
4) "100"
/*取笔试分数最高的前两名*/
127.0.0.1:6379> ZREVRANGE writtenTestScore 0 1 withscores
1) "tom"
2) "100"
3) "tony"
4) "80"
三、运算类操作
127.0.0.1:6379> ZRANGE writtenTestScore 0 -1 withscores
1) "jack"
2) "60"
3) "tony"
4) "80"
5) "tom"
6) "100"
对jack的分值进行加法运算后,jack排到了第二个位置。
127.0.0.1:6379> ZINCRBY writtenTestScore 30 jack
"90"
127.0.0.1:6379> ZRANGE writtenTestScore 0 -1 withscores
1) "tony"
2) "80"
3) "jack"
4) "90"
5) "tom"
6) "100"
也就是说,sorted_set 对集合的每一次变更,都随时维护着一个按分值排序的顺序。
下面来看并集与交集的操作。
为了方便理解交集和并集,我们分别对writtenTestScore和
interview_Score进行增加元素的操作。即polly只参加了笔试,lucy只参加了面试。
127.0.0.1:6379> zadd writtenTestScore 50 polly
(integer) 1
127.0.0.1:6379> ZRANGE writtenTestScore 0 -1 withscores
1) "polly"
2) "50"
3) "tony"
4) "80"
5) "jack"
6) "90"
7) "tom"
8) "100"
127.0.0.1:6379> zadd interviewScore 50 lucy
(integer) 1
127.0.0.1:6379> ZRANGE interviewScore 0 -1 withscores
1) "lucy"
2) "50"
3) "tony"
4) "60"
5) "tom"
6) "70"
7) "jack"
8) "80"
假设现在要根据笔试(权重0.6)和面试(权重0.4)的总分数进行排序,可以用 ZUNIONSTORE 来实现。
127.0.0.1:6379> ZUNIONSTORE result 2 writtenTestScore interviewScore weights 0.6 0.4 aggregate sum
(integer) 5
/*lucy只参加了面试,50*0.4=20; tony笔试80*0.6+面试60*0.4=72*/
127.0.0.1:6379> ZRANGE result 0 -1 withscores
1) "lucy"
2) "20"
3) "polly"
4) "30"
5) "tony"
6) "72"
7) "jack"
8) "86"
9) "tom"
10) "88"
假设需求有变化,增加条件限制:面试和笔试都参加的。此时,可以使用集合交集运算 ZINTERSTORE。
127.0.0.1:6379> ZINTERSTORE result 2 writtenTestScore interviewScore weights 0.6 0.4 aggregate sum
(integer) 3
127.0.0.1:6379> ZRANGE result 0 -1 withscores
1) "tony"
2) "72"
3) "jack"
4) "86"
5) "tom"
6) "88"
关于sorted_set的排序算法
sorted_set对于集合变更的操作,例如zadd,zrem,zpop,zincrby等,都随时维护着一个排序。
简单理解,这里的排序算法是用skipList(跳表)实现的。举个例子,跳表的算法类似于,我们找书上的某一页,会先找到目录一样。当然,跳表的“目录”可能不止一层。
总结
稍微总结一下,本次分享了sorted_set的三种类型的操作,分别是基本操作(包括添加、删除)、统计类(集合中符合某些条件的元素个数、排名、元素列表)操作以及运算类(交集、并集、加法)操作。
sorted_set一般适用于排行榜,例如按照动态的点赞数(作为set中key的分值)、帖子的点击率排名等场景。
简单理解,sorted_set使用跳表实现的。
好了,本次分享就是这样,我们下次见。
关注我,持续输出技术干货。