Redis学习笔记【08】 - 有序集合

一、简介

有序集合相对于哈希、列表、集合来说会有一点点陌生,但既然叫有序集合,那么它和集合必然有着联系,它保留了集合不能有重复成员的特性,但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下标作为排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依据。如图所示,该有序集合包含kris、mike、frank、tim、martin、tom,的分数分别是1、91、200、220、250、251,有序集合提供了获取指定分数和元素范围查询、计算成员排名等功能,合理的利用有序集合,能帮助我们在实际开发中解决很多问题。

Tip:有序集合的元素不能重复,但是score可以重复,就和一个班里的同学学号不能重复,但考试成绩可以相同。

列表、集合和有序集合三者的异同点:

数据结构 是否允许重复元素 是否有序 有序实现方式 应用场景
列表 索引下标 时间轴、消息队列等
集合 标签、社交等
有序集合 分值 排行榜系统、社交等

二、命令

1、集合内

1)添加成员

zadd key score member [score member ...]

下面操作向有序集合user:ranking添加用户tom和他的分数251:

127.0.0.1:6379> zadd user:ranking 251 tom
(integer) 1

返回结果代表成功添加成员的个数。

有关zadd命令有两点需要注意:

  • Redis3.2为zadd命令添加了nx、xx、ch、incr四个选项:
    1.nx: member必须不存在,才可以设置成功,用于添加。
    2.xx. member必须存在,才可以设置成功,用于更新。
    3.ch: 返回此次操作后,有序集合元素和分数发生的个数。
    4.incr: 对score做增加,相当于后面介绍的zincrby。

  • 有序集合相比集合提供了排序字段,但也产生了代价,zadd的时间复杂度为O(log(n)),sadd的时间复杂度为O(1)。

2)计算成员个数

zcard key

和集合类型的scard一样,zcard的时间复杂度为O(1)。

127.0.0.1:6379> zadd user:ranking 1 kris 91 mike 200 frank 220 tim 250 martin
(integer) 5
127.0.0.1:6379> zcard user:ranking
(integer) 6

3)计算某个成员的分数

zscore key member

如果成员不存在则返回nil:

127.0.0.1:6379> zscore user:ranking tom
"251"
127.0.0.1:6379> zscore user:ranking nosee
(nil)

4)计算成员的排名

zrank key member
zrevrank key member

zrank是从分数从低到高返回排名,zrevrank反之。如下操作,tom在zrankt zrevrank分别排名第5和第0(排名从0开始计算)。

127.0.0.1:6379> zrank user:ranking tom
(integer) 5
127.0.0.1:6379> zrevrank user:ranking tom
(integer) 0

5)删除成员

zrem key member [member ...]

返回结果为删除成功的个数,如:

127.0.0.1:6379> zrem user:ranking mike
(integer) 1

6)增加成员的分数

zincrby key increment member

如:

127.0.0.1:6379> zincrby user:ranking 9 tom 
"260"

7)返回指定排名范围的成员

zrange key start end [withscore]
zrevrange key start end [withscore]

有序集合是按照分值排名的,zrange是从高到低返回 ,zrevrange反之。下面代码返回排名最低的是三个成员,如果加上withscore选项,同时会返回成员的分数:

127.0.0.1:6379> zrange user:ranking 0 2 withscores
1) "kris"
2) "1"
3) "frank"
4) "200"
5) "tim"
6) "220"
127.0.0.1:6379> zrevrange user:ranking 0 2 withscores
1) "tom"
2) "260"
3) "martin"
4) "250"
5) "tim"
6) "220"

8)返回指定分数范围的成员

zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key min max [withscores] [limit offset count]

其中zrangebyscore按照分数从低到高返回,zrevrangebyscore反之。例如下面操作从低到高返回200到221分的成员,withscore选项会同时返回每个成员的分数。 [limit offset count]选项可以限制输出的起始位置和个数:

127.0.0.1:6379> zrangebyscore user:ranking 200 221 withscores
1) "frank"
2) "200"
3) "tim"
4) "220"
127.0.0.1:6379> zrevrangebyscore user:ranking  221 200 withscores
1) "tim"
2) "220"
3) "frank"
4) "200"

同时min和max还支持开区间(小括号)和闭区间(中括号),-inf和+inf分别代表无限小和无限大:

127.0.0.1:6379> zrangebyscore user:ranking (200 +inf  withscores
1) "tim"
2) "220"
3) "martin"
4) "250"
5) "tom"
6) "260"

9)返回指定分数范围成员个数

zcount key min max

如:

127.0.0.1:6379> zcount user:ranking 200 221
(integer) 2

10)删除指定排名内的升序元素

zremrangebyrank key start end

下面操作会删除第start到第end名的成员:

127.0.0.1:6379> zremrangebyrank user:ranking 0 2
(integer) 3

11)删除指定分数范围的成员

zremrangebyscore key min max

下面操作将250分以上的成员全部删除,返回结果为成功删除的个数:

127.0.0.1:6379> zremrangebyscore user:ranking (250 +inf
(integer) 1

2、集合间的操作

将下图的两个有序集合导入到Redis中:

127.0.0.1:6379> zadd user:ranking:1 1 kris 91 mike 200 frank 220 tim 250 martin 251 tom 
(integer) 6
127.0.0.1:6379> zadd user:ranking:2 8 james 77 mike 625 martin 888 tom 
(integer) 4

1)交集

zinterstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sun|min|max]

这个命令参数较多,下面分别进行说明:

  • destination:交集计算结果保存到这个键。
  • numkeys:需要做交集计算键的个数。
  • key [key ...]:需要做交集计算的键。
  • weights weight [weight ...]:每个键的权重,在做交集计算时,每个键中的每个member会将自己的分数乘以这个权重,每个键的权重默认是1。
  • aggregate sun|min|max:计算成员交集后,分值可以按sum(和)、min(最小值)、max(最大值)做总汇,默认值是sum。

下面操作对user:ranking:1和user:ranking:2做交集,weights和aggregate使用了默认配置,可以看到目标键user:ranking:1_inter_2对分值做了sum操作:

127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:ranking:1 user:ranking:2 
(integer) 3
127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
1) "mike"
2) "168"
3) "martin"
4) "875"
5) "tom"
6) "1139"

如果想让user:ranking:2的权重变为0.5,并且聚合效果使用max,可以执行如下操作:

127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:ranking:1 user:ranking:2 weights 1 0.5 aggregate max
(integer) 3
127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
1) "mike"
2) "91"
3) "martin"
4) "312.5"
5) "tom"
6) "444"

2)并集

zunionstore destination numkeys key [key ...] [weights weight [weight ...]] [aggregate sun|min|max]

该命令的所有参数和zinterstore是一致的,只不过是做并集计算,例如下面操作是计算user:ranking:1和user:ranking:2的并集:

127.0.0.1:6379> zunionstore user:ranking:1_union_2 2 user:ranking:1 user:ranking:2
(integer) 7
127.0.0.1:6379> zrange user:ranking:1_union_2 0 -1 withscores
 1) "kris"
 2) "1"
 3) "james"
 4) "8"
 5) "mike"
 6) "168"
 7) "frank"
 8) "200"
 9) "tim"
10) "220"
11) "martin"
12) "875"
13) "tom"
14) "1139"

三、有序集合命令的时间复杂度

命令 时间复杂度
zadd key score member [score member ...] O(k*log(n)),k是添加成员的个数,n是当前有序集合成员个数
zcard key O(1)
zscore key member O(1)
zrank/zrevrank key member O(log(n)),n是当前有序集合成员个数
zrem key member [member ...] O(k*log(n)),k是删除成员的个数,n是当前有序集合成员个数
zincrby key increment member O(log(n)),n是当前有序集合成员个数
zrange/zrevrange key start end [withscore] O(log(n)+k),k是要获取的成员个数,n是当前有序集合成员个数
zrangebyscore/zrevrangebyscore key min max [withscores] [limit offset count] O(log(n)+k),k是要获取的成员个数,n是当前有序集合成员个数
zcount key min max O(log(n)),n是当前有序集合成员个数
zremrangebyrank key start end O(log(n)+k),k是要删除的成员个数,n是当前有序集合成员个数
zremrangebyscore key min max O(log(n)+k),k是要删除的成员个数,n是当前有序集合成员个数
zinterstore destination numkeys key [key ...] O(n*k)+O(m*log(m)),n是成员数最小的有序集合成员个数,k是有序集合的个数,m结果集中成员个数
zunionstore destination numkeys key [key ...] O(n)+O(m*log(m)),n是所有有序集合成员个数和,m结果集中成员个数

参考:

《Redis开发与运维》 付磊 & 张益军

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

推荐阅读更多精彩内容