探索 Redis Set:命令、编码与应用实践
set 类型
- 一 . 常见命令
- 1.1 sadd、smembers
- 1.2 sismember
- 1.3 spop、srandmember
- 1.4 smove
- 1.5 srem
- 1.6 集合间操作
- 交集 : sinter、sinterstore
- 并集 : sunion、sunionstore
- 差集 : sdiff、sdiffstore
- 小结
- 二 . 内部编码
- 6.3 应用场景
- 6.3.1 使用 Set 来保存用户的标签
- 6.3.2 使用 Set 计算用户之间的共同好友
- 6.3.3 统计 UV
Hello , 大家好 , 这个专栏给大家带来的是 Redis 系列 ! 本篇文章给大家讲解的是 Redis 中 set 类型的相关内容 , 我们依然会从常见命令、底层编码方式、应用场景三方面给大家介绍 .
本专栏旨在为初学者提供一个全面的 Redis 学习路径,从基础概念到实际应用,帮助读者快速掌握 Redis 的使用和管理技巧。通过本专栏的学习,能够构建坚实的 Redis 知识基础,并能够在实际学习以及工作中灵活运用 Redis 解决问题 .
专栏地址 : Redis 入门实践
正文开始
set 表示集合 , 就是将一些有关联的数据放到一起 , 那他与 list 还是有一定区别的
- 集合中的元素是无序的 , 也就是插入元素的顺序与实际存储的元素顺序不一致 , 但是集合是同一个集合
比如 :
- 对于 list 来说 , [1 , 2 , 3] 和 [3 , 2 , 1] 是两个不同的 list
- 对于 set 来说 , [1 , 2 , 3] 和 [3 , 2 , 1] 是两个相同的 set
- 集合中的元素是不能重复的 , 也就是元素必须唯一
- 和 list 相似 , 集合中的每个元素也需要是 string 类型 , 但是我们仍然可以使用 JSON 这样的格式来去存储一些数据到 string 中
一 . 常见命令
1.1 sadd、smembers
sadd 是将一个或者多个元素添加到集合中 , 要求添加的元素不能重复
语法 : sadd key member1 [member2 …]
与 hash 类型中将 value 叫做 field 类似 , set 中叫做 member
返回值 : 本次操作添加成功的元素的个数
时间复杂度 : O(N) , N 表示要添加的元素个数 . 如果元素个数不多 , 我们可以认为是 O(1)
smembers 可以获取到集合中所有的元素
语法 : smembers key
时间复杂度 : O(1) , N 表示集合中的元素个数 . 如果元素个数不多 , 我们可以认为是 O(1)
那我们就来看一下示例
127.0.0.1:6379> sadd key 1 2 3 4 # 向集合中添加不重复的元素
(integer) 4 # 返回值代表添加成功的个数
127.0.0.1:6379> type key # 获取 key 的类型
set # 此时显示 key 的类型是 set
127.0.0.1:6379> sadd key 5 5 5 5 5 # 向集合中添加重复元素
(integer) 1 # 只会添加成功一条数据
127.0.0.1:6379> smembers key # 获取集合中的所有元素
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
1.2 sismember
sismember 用来判断某个元素是否存在于 set 中
语法 : sismember key member
127.0.0.1:6379> smembers key
1) "1"
2) "2"
3) "3" # 3 存在于集合中
4) "4"
5) "5"
127.0.0.1:6379> sismember key 3 # 判断 3 是否存在于集合中
(integer) 1 # 返回值为 1 代表存在
127.0.0.1:6379> sismember key 999 # 判断 999 是否存在于集合中
(integer) 0 # 返回值为 0 代表不存在
1.3 spop、srandmember
pop 一般表示从末尾删除一个元素 , 但是集合中的元素是无序的 , 那就不知道末尾元素是谁 , 所以 spop 就只能随机删除一个元素
语法 : spop key [count]
count 不写的时候就表示随机删除一个
127.0.0.1:6379> sadd key 1 2 3 4 # 向集合中添加不重复的元素
(integer) 4
127.0.0.1:6379> spop key # 随机删除一个元素
"3" # 返回值代表被删除的元素
127.0.0.1:6379> spop key
"2"
127.0.0.1:6379> spop key
"4"
127.0.0.1:6379> spop key
"1"
# 构造一样的集合, 然后重新删除
127.0.0.1:6379> sadd key 1 2 3 4
(integer) 4
127.0.0.1:6379> spop key # 这次先删除的就是 "1" 了
"1"
127.0.0.1:6379> spop key
"2"
127.0.0.1:6379> spop key
"3"
127.0.0.1:6379> spop key
"4"
127.0.0.1:6379> spop key 2 # 随机删除两个元素
1) "1"
2) "2"
那在官方文档中 , 官方也特意声明了 , 弹出的元素就是随机的
https://redis.io/commands/spop/
然后还提到了一段话
这段话的意思就是 spop 类似于 srandmember , srandmember 的作用是随机返回一个元素而不删除
我们可以类似 pop() 和 peek() 来理解
pop() <-> spop() , peek() <-> srandmember
那我们也来看一下 srandmember 的用法
语法 : srandmember key [count]
127.0.0.1:6379> smembers key
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> srandmember key # 随机获取一个元素但不删除
"1"
127.0.0.1:6379> srandmember key
"3"
127.0.0.1:6379> srandmember key 3 # 随机获取 count 个元素但不删除
1) "4"
2) "2"
3) "1"
1.4 smove
smove 的作用就是将一个元素从源 set 中取出来存储到目标 set 中
语法 : smove source destination member
也就是把 member 从 source 上删除 , 再插入到 destination 中
时间复杂度 : O(1)
127.0.0.1:6379> sadd key1 1 2 3 4 # 向集合中添加 1 2 3 4
(integer) 4
127.0.0.1:6379> sadd key2 5 6 7 8 # 向集合中添加 5 6 7 8
(integer) 4
127.0.0.1:6379> smove key1 key2 1 # 将 key1 中的 1 移动到 key2 中
(integer) 1
127.0.0.1:6379> smembers key1 # 此时 key1 中的 1 就不见了
1) "2"
2) "3"
3) "4"
127.0.0.1:6379> smembers key2
1) "1" # 原来是 key1 中的 1 移动到 key2 中了
2) "5"
3) "6"
4) "7"
5) "8"
那如果我给 key1 里面再添加一个 1 , 然后移动到 key2 中 , 会是什么样的效果呢 ?
127.0.0.1:6379> sadd key1 1 # 重新向 key1 中添加 1
(integer) 1
127.0.0.1:6379> smove key1 key2 1 # 将 1 重新从 key1 中移动到 key2
(integer) 1 # 返回值表示插入成功
127.0.0.1:6379> smembers key1 # 此时 key1 中的 1 又不见了
1) "2"
2) "3"
3) "4"
127.0.0.1:6379> smembers key2
1) "1" # 1 就被移动到了 key2 中
2) "5"
3) "6"
4) "7"
5) "8"
那针对上面的这种情况 , smove 并不会认为这会出错 , 他还是会按照 “先删除 , 再插入” 的策略来执行
那如果我们要移动的元素在 source 中不存在呢 ?
127.0.0.1:6379> smove key1 key2 999 # key1 中并不存在 999
(integer) 0 # 0 表示移动失败
1.5 srem
srem 的作用是用来删除集合中指定的一个或者多个元素
语法 : srem key member1 [member2 …]
返回值 : 删除成功的元素个数
127.0.0.1:6379> sadd key 1 2 3 4 # 向集合中添加不重复的元素
(integer) 4
127.0.0.1:6379> srem key 1 # 删除集合中的 1
(integer) 1 # 返回值代表删除成功的个数
127.0.0.1:6379> srem key 2 3 # 删除集合中的多个元素
(integer) 2
1.6 集合间操作
集合间操作指的就是交集、并集、差集
交集 (inter) : 两个集合共有的元素
并集 (union) : 把多个集合中的数据都集中放在一起 , 如果元素有重复 , 也最终只保留一份
差集 (diff) : A 和 B 做差集 , 就是找出哪些元素 , 在 A 中存在 , 在 B 中不存在
举个例子 : A = [1 , 2 , 3 , 4] , B = [3 , 4 , 5 , 6]
交集 : [3 , 4]
并集 : [1 , 2 , 3 , 4 , 5 , 6]
差集 : A 和 B 做差集 -> [1 , 2] , B 和 A 做差集 -> [5 , 6]
交集 : sinter、sinterstore
sinter 用来获取给定集合中共有的元素
语法 : sinter key1 [key2 …]
此处 , 每个 key 都对应一个集合
返回值 : 共有的元素
时间复杂度 : O(N * M) , N 指的是最小的集合元素个数 , M 指的是最大的集合元素个数
127.0.0.1:6379> sadd key1 1 2 3 4
(integer) 4
127.0.0.1:6379> sadd key2 3 4 5 6
(integer) 4
127.0.0.1:6379> sinter key1 key2 # 获取重复元素
1) "3"
2) "4"
那 Redis 还提供了一个命令 , 也是用来求交集的 , 叫做 sinterstore
语法 : sinterstore destination key1 [key2 …]
它的作用不是直接返回交集 , 而是直接将交集保存到 destination 中
返回值代表交集的个数 , 那要想获取到交集的内容 , 我们就可以直接去查看 destination 里面的内容
127.0.0.1:6379> sinterstore key3 key1 key2 # 将 key1 和 key2 的交集保存到 key3 中
(integer) 2
127.0.0.1:6379> smembers key3 # 想要查询交集是什么就需要通过 smembers 单独查看
1) "3"
2) "4"
并集 : sunion、sunionstore
语法 : sunion key1 [key2 …]
返回值 : 并集的结果数据
时间复杂度 : O(N) , N 指的就是总的元素个数
127.0.0.1:6379> sunion key1 key2 # 求 key1 和 key2 的并集
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
那并集同样给出了另一个命令 , 叫做 sunionstore
语法 : sunionstore destination key1 [key2 …]
它的作用就是将并集的结果存储到 destination 这个集合中
返回值 : 并集的元素个数
127.0.0.1:6379> sunionstore key4 key1 key2 # 将 key1 key2 的并集保存到 key4 中
(integer) 6
127.0.0.1:6379> smembers key4
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
差集 : sdiff、sdiffstore
语法 : sdiff key1 [key2 …]
返回值 : 差集的结果
时间复杂度 : O(N) , N 指的是所有所有元素的长度
127.0.0.1:6379> sdiff key1 key2 # 求 key1 中存在的, 而在 key2 不存在的
1) "1"
2) "2"
127.0.0.1:6379> sdiff key2 key1 # 求 key2 中存在的, 而在 key1 不存在的
1) "5"
2) "6"
同样 , 他也提供了一个额外的版本 : sdiffstore , 他也是将差集保存到 destination 集合中
语法 : sdiffstore destination key1 [key2 …]
127.0.0.1:6379> sdiffstore key5 key1 key2 # 将 key1 中存在的而 key2 中不存在的元素保存到 key5 中
(integer) 2
127.0.0.1:6379> smembers key5
1) "1"
2) "2"
127.0.0.1:6379> sdiffstore key6 key2 key1 # 将 key2 中存在的而 key1 中不存在的元素保存到 key6 中
(integer) 2
127.0.0.1:6379> smembers key6
1) "5"
2) "6"
小结
二 . 内部编码
集合中的内部编码有两种 :
- intset (整数集合)
- hashtable (哈希表)
其中 , intset 也是为了节省空间 , 做出的特定优化 . 当集合中的元素均为整数并且元素个数也不是很多的时候 , 内部就使用 intset 来去存储 .
127.0.0.1:6379> sadd key 1 2 3 4 # 集合元素都为整数 && 元素个数比较少 -> intset
(integer) 4
127.0.0.1:6379> object encoding key
"intset"
127.0.0.1:6379> sadd key HelloWorld # 向集合中添加一个字符串 , 数据类型就变为了 hashtable
(integer) 1
127.0.0.1:6379> object encoding key
"hashtable"
6.3 应用场景
6.3.1 使用 Set 来保存用户的标签
什么叫做标签呢 ?
现在许多产品都有自己的用户画像 , 他们通过一些手段来去分析出你的特征以及喜好 , 再去投其所好 . (也就是大家所说的大数据推送)
那这个用户画像就是我们所谓的标签 .
那这些标签就是一些简短的字符串 , 我们就可以把这些标签添加到 Redis 的 Set 中
那使用 Set 来去存储标签 , 依靠的是 Set 的唯一性和无序性 , 更重要的因素是 Set 非常方便计算交集 , 很方便的就可以找到两个用户的公共标签 . 那基于一些公共标签就可以衍生出一些用户群体 , 方便进行用户之间的交互 .
6.3.2 使用 Set 计算用户之间的共同好友
也是基于集合求交集这样的操作来去计算用户之间的共同好友 .
比如 : QQ 中的共同好友
那还可以根据交集来去进行一些好友推荐 .
比如 : 抖音就会推送你的好友之间共同关注的博主
6.3.3 统计 UV
那什么是 UV 呢 ?
在互联网大厂中 , 主要通过 PV 和 UV 来去衡量用户量或者用户规模
- PV (page view) : 每次打开一个页面 (每次访问服务器) 都会产生一个 PV
- UV (user view) : 每个用户打开一个页面 (每次访问服务器) 都会产生一个 UV . 但是如果同一个用户多次访问 , UV 就不会增加
那 UV 就需要按照用户来进行去重 , 就可以通过 set 中进行去重
今天的文章就到此为止啦 , 有帮助的话还请一键三连~