Redis数据类型 - Sorted sets

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高分”)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容