Redis Zset类型

Redis Zset类型

Zset(有序集合)它是集合的一种,不仅可以保存元素,还可以为每个元素关联一个 double 类型的分数(score),Redis 正是通过分数来为集合中的元素进行从小到大的排序。在 Zset 中,元素的值是唯一的,但分数(score)却可以重复。

有序集合:该有序集合显示了三国中的武将的武力

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

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

Zset命令

zadd

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

语法:

sql 复制代码
ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
  • key: 有序集合的键名。
  • score: 成员的分数。
  • member: 要添加到有序集合的成员。

相关选项

XX:仅仅用于更新已经存在的元素,不会添加新元素。

NX:仅用于添加新元素,不会更新已经存在的元素。

CH:默认情况下,ZADD 返回的是本次添加的元素个数,但指定这个选项之后,就会还包含本次更新的元素的个数。

INCR:此时命令类似 ZINCRBY 的效果,将元素的分数加上指定的分数。此时只能指定⼀个元素和分数。

时间复杂度: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"
#CH选项-包含本次更新的元素的个数
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
# NX-只添加,不更新,只添加了 four 和 five
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
# -inf 无穷小 +inf 无穷大
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"

zcard

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

语法:

sql 复制代码
ZCARD key

时间复杂度:O(1)

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

示例

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

zcount

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

语法:

sql 复制代码
ZCOUNT key min max

时间复杂度: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

zrange

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

语法

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

此处的 [start, stop] 为下标构成的区间. 从 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"

zrevrange

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

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

示例

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"

zrangebyscore

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

语法:

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

时间复杂度: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)

zpopmax

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

语法:

sql 复制代码
ZPOPMAX key [count]

时间复杂度: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"

bzpopmax

zpopmax 的阻塞版本。它会持续阻塞连接,直到有成员可以被弹出或达到超时时间。

语法:

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

timeout: 最大阻塞时间,以秒为单位,表示如果在此时间内没有元素弹出,命令将会超时。

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

返回值 :元素列表,如果在超时之前没有可用的成员,命令将返回 null

示例

bash 复制代码
redis> bzpopmax set1 set2 10 set1 set2 10

这条命令将阻塞等待最多10秒钟,如果 set1set2 中有成员,它将返回其中分数最高的成员。如果超过10秒仍然没有可弹出的成员,命令将超时返回空响应。

zpopmin

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

示例

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"

bzpopmin

zpopmin 的阻塞版本。

示例

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"

zrank

返回指定元素的排名,升序。排名有0开始

语法

sql 复制代码
ZRANK key member

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

返回值:排名。 没有则,返回nil

示例

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)

zrevrank

返回指定元素的排名,降序。排名有0开始

示例

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)

zscore

返回指定元素的分数。

语法

sql 复制代码
ZSCORE key member

时间复杂度:O(1)

返回值:分数。

示例

bash 复制代码
redis> zadd myzset 1 "one"
(integer) 1
redis> zscore myzset "one"
"1"

zrem

删除指定的元素。

语法

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

时间复杂度: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"

zremrangebyrank

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

语法

sql 复制代码
ZREMRANGEBYRANK key start stop

时间复杂度: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"

zremrangebyscore

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

示例

bash 复制代码
redis> zadd myzset 1 "one"
(integer) 1
redis> zadd myzset 2 "two"
(integer) 1
redis> zadd myzset 3 "three"
(integer) 1
# 表示 无穷小到1的范围,不包括2
redis> zremrangebyscore myzset -inf (2   
(integer) 1
redis> zrange myzset 0 -1 withscores
1) "two"
2) "2"
3) "three"
4) "3"

zincrby

为指定的元素的关联分数增加或减少指定的分数值。

语法

sql 复制代码
ZINCRBY key increment member
  • increment 为正数表示增加,负数表示减少

时间复杂度: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"
redis> zincrby myzset -2 "one"    # -2 负数表示减少
"1"
redis> zrange myzset 0 -1 withscores
1) "one"
2) "1"
3) "two"
4) "2"

集合间操作

交集

有序集合的交集操作

zinterstore

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

语法

sql 复制代码
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
  • destination:结果将存储在这个键中。
  • numkeys:要进行交集的输入键(有序集合)的数量。
  • key:一个有序集合的名称。
  • WEIGHTS:一个可选的关键字,后面跟着在交集操作中应用于有序集合的权重。
  • AGGREGATE:一个可选的关键字,指定如何聚合交集元素的分数。可以是 SUMMINMAX

时间复杂度: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
# 交集时,zset1 的元素的分数会乘以 2,而 zset2 的元素的分数会乘以 3。
redis> zinterstore out 2 zset1 zset2 weights 2 3
(integer) 2
redis> zrange out 0 -1 withscores
1) "one"
2) "5"    # 1×2+1×3=2+3=5
3) "two"
4) "10"

并集

有序集合的并集操作

zunionstore

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

语法

sql 复制代码
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
  • destination:结果将存储在这个键中,即新的有序集合的名称。
  • numkeys:要进行并集计算的输入键(有序集合)的数量。
  • key:一个有序集合的名称。
  • WEIGHTS:一个可选的关键字,后面跟着在并集操作中应用于有序集合的权重。
  • AGGREGATE:一个可选的关键字,指定如何聚合并集元素的分数。可以是 SUMMINMAX

时间复杂度: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
# zset1 整体 * 2    zset2 整体 * 3
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"

内部编码

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

  • 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
相关推荐
Minxinbb44 分钟前
MySQL中Performance Schema库的详解(上)
数据库·mysql·dba
mmsx2 小时前
android sqlite 数据库简单封装示例(java)
android·java·数据库
zpjing~.~3 小时前
Mongo 分页判断是否有下一页
数据库
2401_857600953 小时前
技术与教育的融合:构建现代成绩管理系统
数据库·oracle
秋恬意3 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
潇湘秦4 小时前
一文了解Oracle数据库如何连接(1)
数据库·oracle
雅冰石4 小时前
oracle怎样使用logmnr恢复误删除的数据
数据库·oracle
web前端神器4 小时前
mongodb给不同的库设置不同的密码进行连接
数据库·mongodb
从以前4 小时前
Berlandesk 注册系统算法实现与解析
数据库·oracle
Muko_0x7d24 小时前
Mongodb
数据库·mongodb