各位小伙伴们,我们上次讲过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使用跳表实现的。
好了,本次分享就是这样,我们下次见。
请关注风哥公众号,持续输出更多干货技术内容。