Redis - Zset 有序集合

一、基本认识

有序集合相对于字符串、列表、哈希、集合来说会有⼀些陌⽣。它保留了集合不能有重复成员的 特点,但与集合不同的是,有序集合中的每个元素都有⼀个唯⼀的浮点类型的分数(score)与之关 联,着使得有序集合中的元素是可以维护有序性的,但这个有序不是⽤下标作为排序依据⽽是⽤这个分数。

图2-26有序集合

有序集合提供了获取指定分数和元素范围查找、计算成员排名等功能,合理地利⽤有序集合,可 以帮助我们在实际开发中解决很多问题。

有序集合中的元素是不能重复的,但分数允许重复。类⽐于⼀次考试之后,每个⼈⼀定有⼀ 个唯⼀的分数,但分数允许相同。

表2-7列表、集合、有序集合三者的异同点。

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

二、普通命令

2.1、ZADD

添加或者更新指定的元素以及关联的分数到zset中,分数应该符合double类型,+inf/-inf作为正负 极限也是合法的。

ZADD的相关选项:

  • XX:仅仅⽤于更新已经存在的元素,不会添加新元素。
  • NX:仅⽤于添加新元素,不会更新已经存在的元素。
  • CH:默认情况下,ZADD返回的是本次添加的元素个数,但指定这个选项之后,就会还包含本次更 新的元素的个数。
  • INCR:此时命令类似ZINCRBY的效果,将元素的分数加上指定的分数。此时只能指定⼀个元素和 分数。

语法:

bash 复制代码
ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member ...]

**命令有效版本:**1.2.0之后

**时间复杂度:**O(log(N))

**返回值:**本次添加成功的元素个数。

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 1 "uno"
 (integer) 1
 redis> ZADD myzset 2 "two" 3 "three"
 (integer) 2
 redis> ZRANGE myzset 0 -1 WITHSCORES
 1) "one"
 2) "1"
 3) "uno"
 4) "1"
 5) "two"
 6) "2"
 7) "three"
 8) "3"
 redis> ZADD myzset 10 one 20 two 30 three
 (integer) 0
 redis> ZRANGE myzset 0 -1 WITHSCORES
 1) "uno"
 2) "1"
 3) "one"
 4) "10"
 5) "two"
 6) "20"
 7) "three"
 8) "30"
 redis> ZADD myzset CH 100 one 200 two 300 three
 (integer) 3
 redis> ZRANGE myzset 0 -1 WITHSCORES
 1) "uno"
 2) "1"
 3) "one"
 4) "100"
 5) "two"
 6) "200"
 7) "three"
 8) "300"
 redis> ZADD myzset XX 1 one 2 two 3 three 4 four 5 five
 (integer) 0
 redis> ZRANGE myzset 0 -1 WITHSCORES
 1) "one"
 2) "1"
 3) "uno"
 4) "1"
 5) "two"
 6) "2"
 7) "three"
 8) "3"
 redis> ZADD myzset NX 100 one 200 two 300 three 400 four 500 five
 (integer) 2
 redis> ZRANGE myzset 0 -1 WITHSCORES
 1) "one"
 2) "1"
 3) "uno"
 4) "1"
 5) "two"
 6) "2"
 7) "three"
 8) "3"
 9) "four"
 10) "400"
 11) "five"
 12) "500"
 redis> ZADD myzset INCR 10 one
 "11"
 redis> ZRANGE myzset 0 -1 WITHSCORES
 1) "uno"
 2) "1"
 3) "two"
 4) "2"
 5) "three"
 6) "3"
 7) "one"
 8) "11"
 9) "four"
 10) "400"
 11) "five"
 12) "500"
 redis> ZADD myzset -inf "negative infinity" +inf "positive infinity"
 (integer) 2
 redis> ZRANGE myzset 0 -1 WITHSCORES
 1) "negative infinity"
 2) "-inf"
 3) "uno"
 4) "1"
 5) "two"
 6) "2"
 7) "three"
 8) "3"
 9) "one"
 10) "11"
 11) "four"
 12) "400"
 13) "five"
 14) "500"
 15) "positive infinity"
 16) "inf"

2.2、 ZCARD

获取⼀个zset的基数(cardinality),即zset中的元素个数。

语法:

bash 复制代码
ZCARD key

**命令有效版本:**1.2.0之后

**时间复杂度:**O(1)

**返回值:**zset内的元素个数。

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZCARD myzset
 (integer) 2

2.3、ZCOUNT

返回分数在min和max之间的元素个数,默认情况下,min和max都是包含的,可以通过(排除。

语法:

bash 复制代码
 ZCOUNT key min max

**命令有效版本:**2.0.0之后

**时间复杂度:**O(log(N))

**返回值:**满⾜条件的元素列表个数。

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZADD myzset 3 "three"
 (integer) 1
 redis> ZCOUNT myzset -inf +inf
 (integer) 3
 redis> ZCOUNT myzset 1 3
 (integer) 3
 redis> ZCOUNT myzset (1 3
 (integer) 2
 redis> ZCOUNT myzset (1 (3
 (integer) 1

2.4、ZRANGE

返回指定区间⾥的元素,分数按照升序。带上WITHSCORES可以把分数也返回。

语法:

bash 复制代码
ZRANGE key start stop [WITHSCORES]

此处的start, stop 为下标构成的区间.从0开始,⽀持负数

**命令有效版本:**1.2.0之后

**时间复杂度:**O(log(N)+M)

**返回值:**区间内的元素列表。

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZADD myzset 3 "three"
 (integer) 1
 redis> ZRANGE myzset 0 -1 WITHSCORES
 1) "one"
 2) "1"
 3) "two"
 4) "2"
 5) "three"
 6) "3"
 redis> ZRANGE myzset 0 -1
 1) "one"
 2) "two"
 3) "three"
 redis> ZRANGE myzset 2 3
 1) "three"
 redis> ZRANGE myzset -2 -1
 1) "two"
 2) "three"

2.5、ZREVRANGE

返回指定区间⾥的元素,分数按照降序。带上WITHSCORES可以把分数也返回。

备注:这个命令可能在6.2.0之后废弃,并且功能合并到ZRANGE中。

语法:

bash 复制代码
ZREVRANGE key start stop [WITHSCORES]

**命令有效版本:**1.2.0之后

**时间复杂度:**O(log(N)+M)

**返回值:**区间内的元素列表。

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZADD myzset 3 "three"
 (integer) 1
 redis> ZREVRANGE myzset 0 -1 WITHSCORES
 1) "three"
 2) "3"
 3) "two"
 4) "2"
 5) "one"
 6) "1"
 redis> ZREVRANGE myzset 0 -1
 1) "three"
 2) "two"
 3) "one"
 redis> ZREVRANGE myzset 2 3
 1) "one"
 redis> ZREVRANGE myzset -2 -1
 1) "two"
 2) "one"

2.6、ZRANGEBYSCORE

返回分数在min和max之间的元素,默认情况下,min和max都是包含的,可以通过(排除。

**备注:**这个命令可能在6.2.0之后废弃,并且功能合并到ZRANGE中。

语法:

bash 复制代码
ZRANGEBYSCORE key min max [WITHSCORES]

**命令有效版本:**1.0.5之后

**时间复杂度:**O(log(N)+M)

**返回值:**区间内的元素列表。

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZADD myzset 3 "three"
 (integer) 1
 redis> ZRANGEBYSCORE myzset -inf +inf
 1) "one"
 2) "two"
 3) "three"
 redis> ZRANGEBYSCORE myzset 1 2
 1) "one"
 2) "two"
 redis> ZRANGEBYSCORE myzset (1 2
 1) "two"
 redis> ZRANGEBYSCORE myzset (1 (2
 (empty array)

2.7、ZPOPMAX

删除并返回分数最⾼的count个元素。

语法:

bash 复制代码
ZPOPMAX key [count]

**命令有效版本:**5.0.0之后

**时间复杂度:**O(log(N)*M)

**返回值:**分数和元素列表。

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZADD myzset 3 "three"
 (integer) 1
 redis> ZPOPMAX myzset
 1) "three"
 2) "3"

2.8、BZPOPMAX

ZPOPMAX的阻塞版本。

语法:

bash 复制代码
BZPOPMAX key [key ...] timeout

**命令有效版本:**5.0.0之后

**时间复杂度:**O(log(N))

**返回值:**元素列表。

⽰例:

bash 复制代码
 redis> DEL zset1 zset2
 (integer) 0
 redis> ZADD zset1 0 a 1 b 2 c
 (integer) 3
 redis> BZPOPMAX zset1 zset2 0
 1) "zset1"
 2) "c"
 3) "2"

2.10、ZPOPMIN

删除并返回分数最低的count个元素。

语法:

bash 复制代码
ZPOPMIN key [count]

**命令有效版本:**5.0.0之后

**时间复杂度:**O(log(N)*M)

**返回值:**分数和元素列表。

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZADD myzset 3 "three"
 (integer) 1
 redis> ZPOPMIN myzset
 1) "one"
 2) "1"

2.11、BZPOPMIN

ZPOPMIN的阻塞版本。

语法:

bash 复制代码
 BZPOPMIN key [key ...] timeout

**命令有效版本:**5.0.0之后

**时间复杂度:**O(log(N))

**返回值:**元素列表。

⽰例:

bash 复制代码
 redis> DEL zset1 zset2
 (integer) 0
 redis> ZADD zset1 0 a 1 b 2 c
 (integer) 3
 redis> BZPOPMIN zset1 zset2 0
 1) "zset1"
 2) "a"
 3) "0"

2.12、ZRANK

返回指定元素的排名,升序。

语法:

bash 复制代码
ZRANK key member

**命令有效版本:**2.0.0之后

**时间复杂度:**O(log(N))

**返回值:**排名

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZADD myzset 3 "three"
 (integer) 1
 redis> ZRANK myzset "three"
 (integer) 2
 redis> ZRANK myzset "four"
 (nil)

2.13、ZREVRANK

返回指定元素的排名,降序。

语法:

bash 复制代码
ZREVRANK key member

**命令有效版本:**2.0.0之后

**时间复杂度:**O(log(N))

**返回值:**排名

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZADD myzset 3 "three"
 (integer) 1
 redis> ZREVRANK myzset "one"
 (integer) 2
 redis> ZREVRANK myzset "four"
 (nil)

2.14、ZSCORE

返回指定元素的分数。

语法:

bash 复制代码
ZSCORE key member

**命令有效版本:**1.2.0之后

**时间复杂度:**O(1)

**返回值:**分数

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZSCORE myzset "one"
 "1"

2.15、ZREM

删除指定的元素。

语法:

bash 复制代码
ZREM key member [member ...]

**命令有效版本:**1.2.0之后

**时间复杂度:**O(M*log(N))

**返回值:**本次操作删除的元素个数。

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZADD myzset 3 "three"
 (integer) 1
 redis> ZREM myzset "two"
 (integer) 1
 redis> ZRANGE myzset 0 -1 WITHSCORES
 1) "one"
 2) "1"
 3) "three"
 4) "3"

2.16、ZREMRANGEBYRANK

按照排序,升序删除指定范围的元素,左闭右闭。

语法:

bash 复制代码
ZREMRANGEBYRANK key start stop

**命令有效版本:**2.0.0之后

**时间复杂度:**O(log(N)+M)

**返回值:**本次操作删除的元素个数。

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZADD myzset 3 "three"
 (integer) 1
 redis> ZREMRANGEBYRANK myzset 0 1
 (integer) 2
 redis> ZRANGE myzset 0 -1 WITHSCORES
 1) "three"
 2) "3"

2.17、ZREMRANGEBYSCORE

按照分数删除指定范围的元素,左闭右闭

语法:

bash 复制代码
ZREMRANGEBYSCORE key min max

**命令有效版本:**1.2.0之后

**时间复杂度:**O(log(N)+M)

**返回值:**本次操作删除的元素个数。

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZADD myzset 3 "three"
 (integer) 1
 redis> ZREMRANGEBYSCORE myzset -inf (2
 (integer) 1
 redis> ZRANGE myzset 0 -1 WITHSCORES
 1) "two"
 2) "2"
 3) "three"
 4) "3"

2.18、ZINCRBY

为指定的元素的关联分数添加指定的分数值

语法:

bash 复制代码
ZINCRBY key increment member

**命令有效版本:**1.2.0之后

**时间复杂度:**O(log(N))

**返回值:**增加后元素的分数

⽰例:

bash 复制代码
 redis> ZADD myzset 1 "one"
 (integer) 1
 redis> ZADD myzset 2 "two"
 (integer) 1
 redis> ZINCRBY myzset 2 "one"
 "3"
 redis> ZRANGE myzset 0 -1 WITHSCORES
 1) "two"
 2) "2"
 3) "one"
 4) "3"

三、集合间操作

图2-27有序集合的交集操作

3.1、ZINTERSTORE

求出给定有序集合中元素的交集并保存进⽬标有序集合中,在合并过程中以元素为单位进⾏合并,元 素对应的分数按照不同的聚合⽅式和权重得到新的分数。

语法:

bash 复制代码
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight
 [weight ...]] [AGGREGATE <SUM | MIN | MAX>]

**命令有效版本:**2.0.0之后

**时间复杂度:**O(N*K)+O(M*log(M)) N是输⼊的有序集合中,最⼩的有序集合的元素个数;K是输⼊了 ⼏个有序集合;M是最终结果的有序集合的元素个数.

返回值:⽬标集合中的元素个数

⽰例:

bash 复制代码
 redis> ZADD zset1 1 "one"
 (integer) 1
 redis> ZADD zset1 2 "two"
 (integer) 1
 redis> ZADD zset2 1 "one"
 (integer) 1
 redis> ZADD zset2 2 "two"
 (integer) 1
 redis> ZADD zset2 3 "three"
 (integer) 1
 redis> ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3
 (integer) 2
 redis> ZRANGE out 0 -1 WITHSCORES
 1) "one"
 2) "5"
 3) "two"
 4) "10"

图2-28有序集合的并集操作

3.2、 ZUNIONSTORE

求出给定有序集合中元素的并集并保存进⽬标有序集合中,在合并过程中以元素为单位进⾏合并,元 素对应的分数按照不同的聚合⽅式和权重得到新的分数。

语法:

bash 复制代码
 ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE <SUM | MIN | MAX>]

**命令有效版本:**2.0.0之后

**时间复杂度:**O(N)+O(M*log(M)) N是输⼊的有序集合总的元素个数;M是最终结果的有序集合的元素个数.

返回值:⽬标集合中的元素个数

⽰例:

bash 复制代码
 redis> ZADD zset1 1 "one"
 (integer) 1
 redis> ZADD zset1 2 "two"
 (integer) 1
 redis> ZADD zset2 1 "one"
 (integer) 1
 redis> ZADD zset2 2 "two"
 (integer) 1
 redis> ZADD zset2 3 "three"
 (integer) 1
 redis> ZUNIONSTORE out 2 zset1 zset2 WEIGHTS 2 3
 (integer) 3
 redis> ZRANGE out 0 -1 WITHSCORES
 1) "one"
 2) "5"
 3) "three"
 4) "9"
 5) "two"
 6) "10"

3.3、命令⼩结

表2-8有序集合命令

命令 时间复杂度
zadd key score member scoremember... O(k *log(n)),k 是添加成员的个数,n是当前有序集合的元 素个数
zcard key O(1)
zscore key member O(1)
zrank key member 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 key start end withscores zrevrange key start end withscores O(k+log(n)),k是获取成员的个数,n是当前有序集合的元 素个数
zrangebyscore key min max withscores zrevrangebyscore key max min withscores O(k+log(n)),k是获取成员的个数,n是当前有序集合的元 素个数
zcount O(log(n)),n是当前有序集合的元素个数
zremrangebyrank key start end O(k+log(n)),k是获取成员的个数,n是当前有序集合的元 素个数
zremrangebyscore key min max O(k+log(n)),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是⽬标 集合元素个数

四、内部编码

有序集合类型的内部编码有两种:

  • ziplist(压缩列表):当有序集合的元素个数⼩于zset-max-ziplist-entries配置(默认128个), 同时每个元素的值都⼩于zset-max-ziplist-value配置(默认64字节)时,Redis会⽤ziplist来作 为有序集合的内部实现,ziplist可以有效减少内存的使⽤。
  • skiplist(跳表):当ziplist条件不满⾜时,有序集合会使⽤skiplist作为内部实现,因为此时 ziplist的操作效率会下降。

1)当元素个数较少且每个元素较⼩时,内部编码为ziplist:

bash 复制代码
 127.0.0.1:6379> zadd zsetkey 50 e1 60 e2 30 e3
 (integer) 3
 127.0.0.1:6379> object encoding zsetkey
 "ziplist"

2)当元素个数超过128个,内部编码skiplist:

bash 复制代码
 127.0.0.1:6379> zadd zsetkey 50 e1 60 e2 30 e3 ... 省略 ... 82 e129
 (integer) 129
 127.0.0.1:6379> object encoding zsetkey
 "skiplist"

3)当某个元素⼤于64字节时,内部编码skiplist:

bash 复制代码
 127.0.0.1:6379> zadd zsetkey 50 "one string bigger than 64 bytes ... 省略 ..."
 (integer) 1
 127.0.0.1:6379> object encoding zsetkey
 "skiplist"

五、使用场景

有序集合⽐较典型的使⽤场景就是排⾏榜系统。例如常⻅的⽹站上的热榜信息,榜单的维度可能 是多⽅⾯的:按照时间、按照阅读量、按照点赞量。本例中我们使⽤点赞数这个维度,维护每天的热榜:

1)添加⽤⼾赞数

例如⽤⼾james发布了⼀篇⽂章,并获得3个赞,可以使⽤有序集合的zadd和zincrby功能:

bash 复制代码
zadd user:ranking:2022-03-15 3 james

之后如果再获得赞,可以使⽤zincrby:

bash 复制代码
 zincrby user:ranking:2022-03-15 1 james

2)取消⽤⼾赞数

由于各种原因(例如⽤⼾注销、⽤⼾作弊等)需要将⽤⼾删除,此时需要将⽤⼾从榜单中删除掉,可 以使⽤zrem。例如删除成员tom:

bash 复制代码
zrem user:ranking:2022-03-15 tom

3)展⽰获取赞数最多的10个⽤⼾

此功能使⽤zrevrange命令实现:

bash 复制代码
 zrevrangebyrank user:ranking:2022-03-15 0 9

4)展⽰⽤⼾信息以及⽤⼾分数

次功能将⽤⼾名作为键后缀,将⽤⼾信息保存在哈希类型中,⾄于⽤⼾的分数和排名可以使⽤zscore 和zrank来实现

bash 复制代码
 hgetall user:info:tom
 zscore user:ranking:2022-03-15 mike
 zrank user:ranking:2022-03-15 mike
相关推荐
二宝哥几秒前
CentOS 7.9 系统下 Redis 5.0.4 单机安装与源码编译安装指南
redis·centos
oradh1 小时前
Oracle XTTS实现跨版本迁移和升级(Oracle 11g单库升级至19C RAC集群)
数据库·oracle·11g升级19c·xtts跨版本迁移和升级
z123456789862 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
程序猿DD2 小时前
一个 API Key,统一调用大模型、生图和联网搜索
数据库·网关
TlSfoward3 小时前
抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具
数据库·爬虫·网络协议·搜索引擎·php
数智化管理手记3 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发4 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序
SelectDB4 小时前
PostgreSQL 实时同步到 Apache Doris:一站式 CDC 解决方案
数据库
欲速不达88204 小时前
Mysql并发DML锁等待的优化
数据库·mysql
Database_Cool_4 小时前
AI Agent 长期记忆怎么存?阿里云 PolarDB-X 对接 Mem0 记忆框架实践
数据库·阿里云