Sorted sets是一种类似于Sets和hashes的混合体的数据类型。与Sets一样,Sorted sets也是由唯一的、不重复的字符串元素组成,所以在某种意义上,Sorted sets也是Sets。
然而,Sets中的元素是无序的,但是Sorted sets中的每个元素都与一个浮点值相关联,称为score,(这就是Sorted sets与Hashes相似的原因,因为每个元素都映射到一个值(score))。
此外,Sorted sets中的元素是按顺序取的(因此它们不是在请求时排序的,排序是Sorted sets数据结构的一个特性)。它们是按照以下规则排序:
- 如果A和B是两个分数不同的元素,当A.score > B.score时,A > B
- 如果A和B有完全相同的分数,那么如果A字符串在词典顺序上比B字符串大,则A>B。A和B字符串不能相等,因为Sorted sets的元素是唯一的
让我们从一个简单的例子开始,添加一些选定的黑客名字作为sorted sets的元素,他们的出生年份作为“分数”。
127.0.0.1:6379> zadd hackers 1940 "Alan Kay"
(integer) 1
127.0.0.1:6379> zadd hackers 1957 "Sophie Wilson"
(integer) 1
127.0.0.1:6379> zadd hackers 1953 "Richard Stallman"
(integer) 1
127.0.0.1:6379> zadd hackers 1949 "Anita Borg"
(integer) 1
127.0.0.1:6379> zadd hackers 1965 "Yukihiro Matsumoto"
(integer) 1
127.0.0.1:6379> zadd hackers 1914 "Hedy Lamarr"
(integer) 1
127.0.0.1:6379> zadd hackers 1916 "Claude Shannon"
(integer) 1
127.0.0.1:6379> zadd hackers 1969 "Linus Torvalds" 1912 "Alan Turing"
(integer) 2
如您所见,ZADD类似于SADD,但是需要一个额外的参数(放置在要添加的元素之前),即分数(score)。ZADD也是可变的,因此您可以随意地指定多个分数-值对。
对于sorted sets来说,返回一个按出生年份排序的黑客列表是很简单的,因为实际上他们已经是排好序的了。
注意:sorted sets是通过双端口数据(dual-ported data structure)结构实现的,它包含一个跳跃列表(skip list)和一个哈希表(hashes table),因此每当我们添加一个元素时,Redis都会执行一个O(log(N))操作。但是当我们请求排序的元素时,Redis不需要做任何工作,它已经是排好序了:
127.0.0.1:6379> zrange hackers 0 -1
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"
6) "Richard Stallman"
7) "Sophie Wilson"
8) "Yukihiro Matsumoto"
9) "Linus Torvalds"
如果我想以相反的方式排序,从最小的到最大的?使用ZREVRANGE代替ZRANGE:
127.0.0.1:6379> ZREVRANGE hackers 0 -1
1) "Linus Torvalds"
2) "Yukihiro Matsumoto"
3) "Sophie Wilson"
4) "Richard Stallman"
5) "Anita Borg"
6) "Alan Kay"
7) "Claude Shannon"
8) "Hedy Lamarr"
9) "Alan Turing"
也可以使用WITHSCORES参数来返回分数:
127.0.0.1:6379> zrange hackers 0 -1 withscores
1) "Alan Turing"
2) "1912"
3) "Hedy Lamarr"
4) "1914"
5) "Claude Shannon"
6) "1916"
7) "Alan Kay"
8) "1940"
9) "Anita Borg"
10) "1949"
11) "Richard Stallman"
12) "1953"
13) "Sophie Wilson"
14) "1957"
15) "Yukihiro Matsumoto"
16) "1965"
17) "Linus Torvalds"
18) "1969"
操作指定范围
Sorted sets比这更强大。它们可以在一定范围内工作。我们要获取所有出生在1950年之前的人。可以使用ZRANGEBYSCORE命令来实现:
127.0.0.1:6379> ZRANGEBYSCORE hackers -inf 1950 withscores
1) "Alan Turing"
2) "1912"
3) "Hedy Lamarr"
4) "1914"
5) "Claude Shannon"
6) "1916"
7) "Alan Kay"
8) "1940"
9) "Anita Borg"
10) "1949"
我们要求Redis返回所有分数在-∞到1950之间的元素(包括两端)。
也可以删除指定范围内的元素。让我们把1940年到1960年出生的所有黑客从sorted sets中删除:
127.0.0.1:6379> zremrangebyscore hackers 1940 1960
(integer) 4
ZREMRANGEBYSCORE可能不是最好的命令名字,但它可能非常有用,并返回删除元素的数量。
Sorted sets的另一个非常有用的操作是get-rank操作。可以获取一个元素在有序集合中的位置。
127.0.0.1:6379> zrank hackers "Hedy Lamarr"
(integer) 1
可以使用ZREVRANK命令来获得元素降序排序时的位置。
字典分数(Lexicographical scores)
新版本Redis 2.8,引入了一个新特性,允许按字典顺序获取指定范围(的元素),假设一个sorted set中的所有元素的分数都相同(元素使用C的memcmp函数来比较,所以它可以保证不需要校对,每个实例将输出相同的结果)。
使用字典顺序操作的主要命令有ZRANGEBYLEX、ZREVRANGEBYLEX、ZREMRANGEBYLEX和ZLEXCOUNT。
例如,让我们再次添加我们的著名黑客名单,但这一次对所有元素使用分数都为:
127.0.0.1:6379> zadd hackers 0 "Alan Kay" 0 "Sophie Wilson" 0 "Richard Stallman" 0 "Anita Borg" 0 "Yukihiro Matsumoto" 0 "Hedy Lamarr" 0 "Claude Shannon" 0 "Linus Torvalds" 0 "Alan Turing"
(integer) 9
根据sorted sets的排序规则,它们已经按字典顺序进行了排序:
127.0.0.1:6379> zrange hackers 0 -1
1) "Alan Kay"
2) "Alan Turing"
3) "Anita Borg"
4) "Claude Shannon"
5) "Hedy Lamarr"
6) "Linus Torvalds"
7) "Richard Stallman"
8) "Sophie Wilson"
9) "Yukihiro Matsumoto"
使用ZRANGEBYLEX我们可以查看指定的字典范围:
127.0.0.1:6379> zrangebylex hackers [B [P
1) "Claude Shannon"
2) "Hedy Lamarr"
3) "Linus Torvalds"
范围可以包含,也可以不包含(取决于第一个字符),并且用 + 和 - 字符串分别代表正无穷(infinite)和负无穷(- infinite)。
127.0.0.1:6379> ZRANGEBYLEX hackers - + #获取所有元素
1) "Alan Kay"
2) "Alan Turing"
3) "Anita Borg"
4) "Claude Shannon"
5) "Hedy Lamarr"
6) "Linus Torvalds"
7) "Richard Stallman"
8) "Sophie Wilson"
9) "Yukihiro Matsumoto"
这个特性很重要,因为它允许我们使用sorted sets作为通用索引。例如,如果您想用128位无符号整数作参数对元素进行索引,那么您所需要做的就是将元素添加到具有相同分数(例如0),但具有16字节前缀(由采用big endian表示的128比特的数字组成)的sorted sets中。由于big endian里的数字按照字典顺序(按原始字节顺序)等同于按照数字顺序排列,所以您可以查看128位空间中的指定范围,然后将得到的元素的值的前缀舍弃即可。
更新分数:排行榜
注意,sorted sets的分数可以随时更新。只需对已包含在排序集中的元素调用ZADD,就可以用O(log(N))时间复杂度的操作更新其分数(和位置)。因此,当有大量的更新时,sorted sets是适用的。
对于这一特性,一个常见的用例是排行榜。典型的应用程序是Facebook游戏,你可以根据用户的高分进行排序,再加上get-rank操作,以显示排名前n的用户,以及排行榜中的用户排名(例如,“你是这里的第4932高分”)。