探究Redis 05:有序集合

Redis有序集合(Sorted Sets)

Redis中的有序集合类似集合与哈希表的混合体。和集合一样,有序集合中同样存储了一组互不相同的字符串元素。在某些场景下,有序集合使用时就可以被看做是一种集合。

与集合不同的是,有序集合中的每个元素还与一个额外的浮点数关联,称为评分 (类似哈希表,元素与一个值关联)。

此外,有序集合中的元素都是按序排列的。排序规则如下:

  • 如果A与B两个元素评分不同,如果A的评分大于B的评分,那么A大于B。
  • 如果A与B的评分相同, 如果A字典排序大于B,那么A大于B。
  • 在有序集合中A和B不能相等,以保证集合中元素的唯一性。

让我们先看这样一个示例:示例中有序集合记录了一组黑客人员名单,他们的姓名作为排序元素,而出生年份作为元素评分。

> zadd hackers 1940 "Alan Kay"
(integer) 1
> zadd hackers 1957 "Sophie Wilson"
(integer) 1
> zadd hackers 1953 "Richard Stallman"
(integer) 1
> zadd hackers 1949 "Anita Borg"
(integer) 1
> zadd hackers 1965 "Yukihiro Matsumoto"
(integer) 1
> zadd hackers 1914 "Hedy Lamarr"
(integer) 1
> zadd hackers 1916 "Claude Shannon"
(integer) 1
> zadd hackers 1969 "Linus Torvalds"
(integer) 1
> zadd hackers 1912 "Alan Turing"
(integer) 1

可以看到ZADD类似SADD命令,向有序集合添加元素,同时为每个元素附加一个评分。ZADD 也支持一次添加多个元素到集合中,虽然上面的例子没有体现。

对于有序集合,返回按出生年份排序的黑客列表是很简单的,因为实际上他们存储时已经排序了。
需要说明的是,有序集合是通过包含跳表和哈希表的双端口数据结构实现的,因此每次添加元素时,都需执行O(log(N))操作。这有一定开销,但是当需要获取排序元素时,则不数据不再需要排序操作,只需直接返回就好了:

> 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"

注意:上面的0和-1下标表示从下标为0的元素开始到最后一个元素全部返回(-1这种表达方式类似LRANGE 命令)。

如果需要获取逆序排列的元素呢?你可以使用ZREVRANGE替换ZRANGE命令:

> 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参数,可以同时返回评分结果:

> 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"

使用区间操作

有序集合的功能不止如此,它还支持区间操作。想要查出1950年之前出生的人员列表,可以使用ZRANGEBYSCORE 命令:

> zrangebyscore hackers -inf 1950
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"

上面示例中,我们让Redis返回了评分在负无穷到1950之间的数据 (闭区间)。

也可以移除某个区间内的元素。例如:我们可以从集合中移除出生在1940到1960之间的黑客:

> zremrangebyscore hackers 1940 1960
(integer) 4

ZREMRANGEBYSCORE 命令可能命名比较复杂,但却十分管用,移除的内容会被当做返回结果。
为有序集合元素定义的另一个非常有用的行为是get-rank操作。它可以询问元素在有序集合中的排序下标位置。

> zrank hackers "Anita Borg"
(integer) 4

使用ZREVRANK 命令也可以方便的获取元素的逆序排名(从大到小)。

字典评分

从2.8版本开始, 当有序集合中的元素被赋予相同的评分插入集合时,Redis支持元素按字典排序和并支持相应的区间操作。(元素通过C语言 memcmp函数比较来消除排序歧义和冲突)。

与字典区间操作有关的命令包括: ZRANGEBYLEXZREVRANGEBYLEXZREMRANGEBYLEXZLEXCOUNT

例如,让我们再次添加之前的著名黑客列表,但这次对所有元素指定评分为0:

> 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"

由于有序集合的排序规则,这些元素现在已经安字典顺序排序:

> 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 命令,我们可以获取字典范围的数据集合:

> zrangebylex hackers [B [P
1) "Claude Shannon"
2) "Hedy Lamarr"
3) "Linus Torvalds"

通过括号表示开闭区间,字符串无限大,和负无穷分别用 +- 表示. 详情参见文档。

这个特性很重要,因为它允许我们将有序集合当做通用索引使用。例如,如果要通过128位无符号整数对元素进行索引,只需将元素添加到具有相同评分(例如0)但具有16字节前缀(由big endian中的128位数字组成)的有序集合中。由于big-endian中的数字在按字典顺序排列时实际上也是按数字顺序排列的,因此可以在128位空间中进行范围查询,在获取元素值后丢弃前缀。

一个真实的场景演示可以参见这里: Redis自动完成示例

更新评分:排行榜应用

最后要说明的一点是:有序集合中,元素的分数可以随时更新。只要对已经包含在排序集中的元素调用ZADD,就会用O(log(N))时间更新其得分和排序位置。因为开销很小,有序集合也可以被用在有大量更新操作的场景下。

由于这种特性,有序集合的一个常见的使用场景是得分榜。典型的应用程序可能是一个Facebook游戏中,通过有序集合支持按用户得分排序,获取用户排名等操作,以便支持显示玩家游戏排名,和TOP 10玩家姓名。

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