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
相关推荐
NCIN EXPE19 小时前
redis 使用
数据库·redis·缓存
MongoDB 数据平台19 小时前
为编码代理引入 MongoDB 代理技能和插件
数据库·mongodb
极客on之路20 小时前
mysql explain type 各个字段解释
数据库·mysql
代码雕刻家20 小时前
MySQL与SQL Server的基本指令
数据库·mysql·sqlserver
lThE ANDE20 小时前
开启mysql的binlog日志
数据库·mysql
hERS EOUS20 小时前
nginx 代理 redis
运维·redis·nginx
yejqvow1220 小时前
CSS如何控制placeholder文字的颜色_使用--placeholder伪元素
jvm·数据库·python
oLLI PILO20 小时前
nacos2.3.0 接入pgsql或其他数据库
数据库
m0_7436239220 小时前
HTML怎么创建多语言切换器_HTML语言选择下拉结构【指南】
jvm·数据库·python