【Redis】集合类型详解及实际应用场景分析:从命令操作到内部编码


Set 集合

集合类型也是保存多个字符串类型的元素的,但和列表类型不同的是,集合中:

  1. 元素之间是无序

这里的无序,是指变换一下元素的顺序,集合还是一样的集合,而不是说顺序逆序的无序

对于 list 来说,[1,2,3] 和 [2,1,3] 是不等价的,而对于 set 来说,是等价的

  1. 元素不允许重复

如图 2-24 所⽰。

⼀个集合中最多可以存储 2^32 - 1 个元素。Redis 除了⽀持集合内的增删查改操作,同时还⽀持多个集合取交集、并集、差集,合理地使⽤好集合类型,能在实际开发中解决很多问题。

图 2-24 集合类型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

普通命令

SADD

sadd

集合中的元素叫 member,哈希中的元素叫 field

将⼀个或者多个元素添加到 set 中。注意,重复的元素⽆法添加到 set 中。

语法:

SADD key member [member ...]

命令有效版本:1.0.0 之后

时间复杂度:O(1)

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

⽰例:

redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SADD myset "World"
(integer) 0
redis> SMEMBERS myset
1) "Hello"
2) "World"

SMEMBERS

smembers

获取⼀个 set 中的所有元素,注意,元素间的顺序是⽆序的。

语法:

SMEMBERS key

命令有效版本:1.0.0 之后

时间复杂度:O(N)

返回值:所有元素的列表。

⽰例:

redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SMEMBERS myset
1) "Hello"
2) "World"

SISMEMBER

sismember

判断⼀个元素在不在 set 中。

语法:

SISMEMBER key member

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:1 表⽰元素在 set 中。0 表⽰元素不在 set 中或者 key 不存在。

⽰例:

redis> SADD myset "one"
(integer) 1
redis> SISMEMBER myset "one"
(integer) 1
redis> SISMEMBER myset "two"
(integer) 0

SCARD

scard

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

语法:

SCARD key

命令有效版本:1.0.0 之后

时间复杂度:O(1)

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

⽰例:

redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SCARD myset
(integer) 2

SPOP

spop

一般 pop 是从末尾删除一个元素,但集合是无序的,因此这里是随机删除

从 set 中删除并返回⼀个或者多个元素。注意,由于 set 内的元素是⽆序的,所以取出哪个元素实际是未定义⾏为,即随机删除,这里是真随机,而不是伪随机

在源码中,针对 spop 实现时,就采取了“生成随机数”的方式

srandommember 就是随机获取一个元素,但不删除

语法:

SPOP key [count]

命令有效版本:1.0.0 之后

时间复杂度:O(N), n 是 count

返回值:取出的元素。

⽰例:

redis> SADD myset "one"
(integer) 1
redis> SADD myset "two"
(integer) 1
redis> SADD myset "three"
(integer) 1
redis> SPOP myset
"one"
redis> SMEMBERS myset
1) "three"
2) "two"
redis> SADD myset "four"
(integer) 1
redis> SADD myset "five"
(integer) 1
redis> SPOP myset 3
1) "three"
2) "four"
3) "two"
redis> SMEMBERS myset
1) "five"

SMOVE

smove

将⼀个元素从源 set 取出并放⼊⽬标 set 中。

语法:

 SMOVE source destination member

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:1 表⽰移动成功,0 表⽰失败。

如果 set1 和 set2 中都有一个 1,此时让 set1 的 1 移动到 set2 中,最终返回值是 1,即移动成功,set1 的 1 不见了,set2 的 1 也还在

⽰例:

redis> SADD myset "one"
(integer) 1
redis> SADD myset "two"
(integer) 1
redis> SADD myotherset "three"
(integer) 1
redis> SMOVE myset myotherset "two"
(integer) 1
redis> SMEMBERS myset
1) "one"
redis> SMEMBERS myotherset
1) "three"
2) "two"

SREM

srem

将指定的元素从 set 中删除。

语法:

SREM key member [member ...]

命令有效版本:1.0.0 之后

时间复杂度:O(N), N 是要删除的元素个数.

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

⽰例:

redis> SADD myset "one"
(integer) 1
redis> SADD myset "two"
(integer) 1
redis> SADD myset "three"
(integer) 1
redis> SREM myset "one"
(integer) 1
redis> SREM myset "four"
(integer) 0
redis> SMEMBERS myset
1) "three"
2) "two"
集合间操作

**交集(inter)、并集(union)、差集(diff)**的概念如图 2-25 所⽰。

交集和并集的多个集合的顺序无所谓,但差集的多个集合的顺序就有所谓了,具体是 A-B 还是 B-A

图 2-25 集合求交集、并集、差集

SINTER

sinter

获取给定 set 的交集中的元素。

语法:

SINTER key [key ...]

命令有效版本:1.0.0 之后

时间复杂度:O(N * M), N 是最⼩的集合元素个数. M 是最⼤的集合元素个数.

返回值:交集的元素。

⽰例:

redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SINTER key1 key2
1) "c"

SINTERSTORE

sinterstore

获取给定 set 的交集中的元素并保存到⽬标 set 中。

语法中第一个 key 是目标 set

语法:

SINTERSTORE destination key [key ...]

命令有效版本:1.0.0 之后

时间复杂度:O(N * M), N 是最⼩的集合元素个数. M 是最⼤的集合元素个数.

返回值:交集的元素个数。

⽰例:

redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SINTERSTORE key key1 key2
(integer) 1
redis> SMEMBERS key
1) "c"

SUNION

sunion

获取给定 set 的并集中的元素。

语法:

SUNION key [key ...]

命令有效版本:1.0.0 之后

时间复杂度:O(N), N 给定的所有集合的总的元素个数.

返回值:并集的元素。

⽰例:

redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SUNION key1 key2
1) "a"
2) "c"
3) "e"
4) "b"
5) "d"

SUNIONSTORE

sunionstore

获取给定 set 的并集中的元素并保存到⽬标 set 中。

语法:

SUNIONSTORE destination key [key ...]

命令有效版本:1.0.0 之后

时间复杂度:O(N), N 给定的所有集合的总的元素个数.

返回值:并集的元素个数。

⽰例:

redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SUNIONSTORE key key1 key2
(integer) 5
redis> SMEMBERS key
1) "a"
2) "c"
3) "e"
4) "b"
5) "d"

SDIFF

sdiff

获取给定 set 的差集中的元素。

语法:

SDIFF key [key ...]

注意是第一个 key 对第二个 key 的差集,即 A-B 与 B-A 不同

命令有效版本:1.0.0 之后

时间复杂度:O(N), N 给定的所有集合的总的元素个数.

返回值:差集的元素。

⽰例:

redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SDIFF key1 key2
1) "a"
2) "b"

SDIFFSTORE

sdiffstore

获取给定 set 的差集中的元素并保存到⽬标 set 中。

语法:

SDIFFSTORE destination key [key ...]

命令有效版本:1.0.0 之后

时间复杂度:O(N), N 给定的所有集合的总的元素个数.

返回值:差集的元素个数。

⽰例:

redis> SADD key1 "a"
(integer) 1
redis> SADD key1 "b"
(integer) 1
redis> SADD key1 "c"
(integer) 1
redis> SADD key2 "c"
(integer) 1
redis> SADD key2 "d"
(integer) 1
redis> SADD key2 "e"
(integer) 1
redis> SDIFFSTORE key key1 key2
(integer) 2
redis> SMEMBERS key
1) "a"
2) "b"
命令小结

表 2-6 总结了集合类型的常⻅命令,开发⼈员可以根据⾃⾝需求进⾏选择。

表 2-6 集合类型命令

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

内部编码

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

  • intset(整数集合):当集合中的元素都是整数并且元素的个数⼩于 set-max-intset-entries 配置(默认 512 个)时,Redis 会选⽤ intset 来作为集合的内部实现,从⽽减少内存的使⽤。
  • hashtable(哈希表):当集合类型⽆法满⾜ intset 的条件时,Redis 会使⽤ hashtable 作为集合的内部实现。
  1. 当元素个数较少并且都为整数时,内部编码为 intset:
127.0.0.1:6379> sadd setkey 1 2 3 4
(integer) 4
127.0.0.1:6379> object encoding setkey
"intset"

  1. 当元素个数超过 512 个,内部编码为 hashtable:
127.0.0.1:6379> sadd setkey 1 2 3 4
(integer) 513
127.0.0.1:6379> object encoding setkey
"hashtable"
  1. 当存在元素不是整数时,内部编码为 hashtable:
127.0.0.1:6379> sadd setkey a
(integer) 1
127.0.0.1:6379> object encoding setkey
"hashtable"
使⽤场景

集合类型⽐较典型的使⽤场景是标签(tag)。例如 A ⽤⼾对娱乐、体育板块⽐较感兴趣,B ⽤⼾对历史、新闻⽐较感兴趣,这些兴趣点可以被抽象为标签。有了这些数据就可以得到喜欢同⼀个标签的⼈,以及⽤⼾的共同喜好的标签,这些数据对于增强⽤⼾体验和⽤⼾黏度都⾮常有帮助。 例如⼀个电⼦商务⽹站会对不同标签的⽤⼾做不同的产品推荐。

下⾯的演⽰通过集合类型来实现标签的若⼲功能。

  1. 给⽤⼾添加标签
sadd user:1:tags tag1 tag2 tag5
sadd user:2:tags tag2 tag3 tag5
...
sadd user:k:tags tag1 tag2 tag4
  1. 给标签添加⽤⼾
sadd tag1:users user:1 user:3
sadd tag2:users user:1 user:2 user:3
...
sadd tagk:users user:1 user:4 user:9 user:28
  1. 删除⽤⼾下的标签
srem user:1:tags tag1 tag5
...
  1. 删除标签下的⽤⼾
srem tag1:users user:1
srem tag5:users user:1
...
  1. 计算⽤⼾的共同兴趣标签
sinter user:1:tags user:2:tags

集合 set 还有多个应用场景:比如去重

一个互联网产品如何衡量用户量,用户规模?

  1. PV,page view,用户每次访问该服务器,都会产生一个 pv
  2. UV,user view,每个用户访问服务器都会产生一个 uv,但同个用户多次访问不会增加 uv
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值