有序集合
有序集合相对于哈希、列表、集合来说会有一点点陌生,但既然叫有序集合,那么它和集合必然有着联系,它保留了集合不能有重复成员的特性,但不同的是,有序集合的元素可以排序。
但是它和列表使用索引下标作为排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依据。
有序集合提供了获取指定分数和元素范围查询、计算成员排名等功能,合理的利用有序集合,能帮助我们在实际开发中解决很多问题
开发提示:
有序集合中的元素不能重复,但是 score 可以重复,就和一个班里的同学学号不能重复,但是考试成绩可以相同
命令
集合内
添加成员
zadd key score member [socre member...]
下面操作向有序集合 user:ranking
添加用户 tom 和他的分数 251:
1 2 |
|
返回结果代表成功添加成员的个数:
1 2 |
|
有关 zadd 命令有两点需要注意:
- Redis3.2 为 zadd 命令添加了 nx、xx、ch、incr 四个选项
- nx: member 必须不存在,才可以设置成功,用于添加
- xx: member 必须存在,才可以设置成功,用于更新
- ch: 返回此操作后,有序集合元素和分数发生变化的个数
- incr: 对 score 做增加、相当于后面介绍的 zincrby
- 有序集合相比集合提供了排序字段,但是也产生了代价,zadd 的时间复杂度为 O(log(n)),sadd 的时间复杂度为 O(1)
计算成员个数
zcard key
例如下面操作返回有序集合 user:ranking
的成员数为 5,和集合类型的 scard 命令一样,zcard 的时间复杂度为 O(1)
1 2 |
|
获取某个成员的分数
zscore key member
tom 的分数为 251,如果成员不存在则返回 nil:
1 2 3 4 |
|
计算成员的排名
1 2 |
|
zrank 是从分数从低到高返回排名,zrevrank 反之。
1 2 3 4 |
|
删除成员
zrem key member [member ...]
下面操作将成员 mike 从有序集合 user:ranking
中删除
1 2 |
|
返回结果为成功删除的个数
增加成员的分数
zincrby key increment member
下面操作给 tom 增加了 9 分,分数变为了 260 分:
1 2 |
|
返回指定排名范围的成员
1 2 |
|
有序集合是按照分值排名的,zrange 是从低到高返回,zrevrange 反之。
下面代码返回排名最低的是三个成员,如果加上 withscores 选项,同时会返回成员的分数:
1 2 3 4 5 6 7 |
|
1 2 3 4 5 6 7 |
|
返回指定分数范围的成员
1 2 |
|
其中 zrangebyscore 按照分数从低到高返回,zrevrangebyscore 反之。
例如下面操作从低到高返回 200 到 221 分的成员,withscores 选项会同时返回每个成员的分数。
[limit offset count]
选项可以限制输出的起始位置和个数
1 2 3 4 5 6 7 8 9 |
|
1 2 3 4 5 6 7 8 9 10 |
|
同时 min 和 max 还支持开区间(小括号)和闭区间(中括号),-inf 和 +inf 分别代表无限小和无限大:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
返回指定分数范围成员个数
zcount key min max
下面操作返回 200 到 221 分的成员的个数
1 2 |
|
删除指定排名内的升序元素
zremrangebyrank key start end
下面操作删除第 srart 到第 end 名的成员
1 2 |
|
删除指定分数范围的成员
zremrangebyscore key min max
下面操作将 250 分以上的成员全部删除,返回结果为成功删除的个数
1 2 |
|
集合间的操作
1 2 3 4 |
|
交集
zinterstore destination numkeys key [key...] [weights weight [weight ...]] [aggregate sum | min | max]
这个命令参数较多,下面分别进行说明:
- destination: 交集计算结果保存到这个键
- numkeys: 需要做交集计算键的个数
key [key...]
: 需要做交集计算的键weights weight[weight...]
: 每个键的权重,在做交集的计算时,每个键中的每个 member 会将自己分数乘以这个权重,每个键的权重默认是 1aggregate sum | min | max
: 计算成员交集后,分值可以按照 sum(和)、min(最小值)、max(最大值)做汇总,默认值是 sum
下面操作对 user:ranking:1
和 user:ranking:2
做交集,weights 和 aggregate 使用了默认配置,可以看到目标键 user:ranking:1_inter_2
对分值做了 sum 操作:
1 2 3 4 5 6 7 8 9 |
|
如果想让 user:ranking:2
的权重变为 0.5,并且聚合效果使用 max,可以执行如下操作:
1 2 3 4 5 6 7 8 9 |
|
并集
zunionstore destination numkeys key [key...] [weights weight [weight...]] [aggregate sum | min | max]
该命令的所有参数和 zinterstore 是一致的,只不过是做并集计算,例如下面操作是计算 user:ranking:1
和 user:ranking:2
的并集,weights 和 aggregate 使用了默认配置,可以看到目标键 user:ranking:1_union_2
对分值做了 sum 操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
内部编码
ziplist(压缩列表)
当有序集合的元素个数小于 zset-max-ziplist-entries 配置(默认 128 个),同时每个元素的值都小于 zset-max-ziplist-value 配置(默认 64 字节)时,Redis 会用 ziplist 来作为有序集合的内部实现,ziplist 可以有效减少内存的使用。
skiplist(跳跃表)
当 ziplist 条件不满足时,有序集合会使用 skiplist 作为内部实现,因为此时 ziplist 的读写效率会下降
使用场景
有序集合比价典型的使用场景就是排行榜系统。
例如视频网站需要对用户上传的视频做排行榜,榜单的维度可能是多个方面的: 按照时间、按照播放量、按照获得的赞数。
下面使用赞数这个维度,记录每天用户上传视频的排行榜。主要需要实现以下 4 个功能
(1) 添加用户赞数
例如用户 mike 上传了一个视频,并获得了 3 个赞,可以使用有序集合的 zadd 和 zincrby 功能:
zadd user:ranking:2016_03_15 mike 3
如果之后再获得一个赞,可以使用 zincrby:
1 |
|
(2) 取消用户赞数
由于各种原因(例如用户注销、用户作弊)需要将用户删除,此时需要将用户从榜单中删除掉,可以使用 zrem。例如删除成员 tom:
1 |
|
(3) 展示获取赞数最多的十个用户
此功能使用 zrevrange 命令实现:
1 |
|
(4) 展示用户信息以及用户信息
此功能将用户名作为键后缀,将用户信息保存在哈希类型中,至于用户的分数和排名可以使用 zscore 和 zrank 两个功能:
1 2 3 4 |
|