【Redis】set 类型

set

一. set 类型介绍

  • 集合就是把一些有关联的数据放到一起,保存多个字符串类型的元素的 (可以使用 JSON 这样的格式,让 string 存储结构化数据),但和列表类型不同的是:
    • 元素之间是无序的。
      • 此处说的无序和之前 list 说的有序是对应的。
      • 有序:顺序很重要,变换一下顺序,就是不同的列表。
      • 无序:顺序不重要,变换一下顺序,还是那个集合。
      • list:1, 2, 32, 1, 3 是不同的 list
      • set:1, 2, 32, 1, 3 是相同的 set
    • 元素不允许重复。
    • 一个集合中最多可以存储 2^32 - 1 个元素。
  • Redis 除了支持集合内的增删查改操作,同时还支持多个集合取交集、并集、差集,合理地使用好集合类型,能在实际开发中解决很多问题。

集合类型:

二. set 命令

在 set 中的元素叫做 member,就像在 hash 中的元素叫做 field、value 类似。

sadd、smembers、sismember

  • sadd:添加⼀/多个元素到 set 中。注意:重复的元素无法添加到集合中。
  • 语法:sadd key member [member ...]
  • 时间复杂度:添加一个元素是 O(1),添加 N 个元素是 O(N)
  • 返回值:本次添加成功的元素个数。
  • smembers:获取一个 set 中的所有元素。注意:元素间的顺序是无序的。
  • 语法:smembers key
  • 时间复杂度:O(N),N 是集合中元素的个数。
  • 返回值:所有元素的列表。
  • sismember:判断一个元素在不在 set 中。
  • 语法:sismember key member
  • 时间复杂度:O(1)
  • 返回值:元素在 set 中,返回 1;元素不在 set 中或者 key 不存在,返回 0

scard、spop、srandmember

  • scard:获取一个 set 的基数 (cardinality),即 set 中的元素个数。
  • 语法:scard key
  • 时间复杂度:O(1)
  • 返回值:set 内的元素个数。
  • spop:从 set 中删除并返回一个或者多个元素。注意:由于 set 内的元素是无序的,所以取出哪个元素实际是未定义行为,即可以看作随机的。
  • 语法:spop key [count]
  • 时间复杂度:O(N),N 是 count 的个数。
  • 返回值:取出的元素。
  • srandmember:从 set 中返回一个或者多个随机的元素。
  • 语法:srandmember key [count]
  • 时间复杂度:O(N),N 是 count 的个数。
  • 返回值:取出的元素。

在 Redis 源码中,针对 spop 实现的时候,就采取了 "生成随机数" srandmember 的方式。

smove、srem

  • smove:将一个元素从源 set 取出并放入目标 set 中。
  • 语法:smove source destination member
  • 时间复杂度:O(1)
  • 返回值:移动成功时,返回 1;移动失败时,返回 0

如果我给 key1 里再添加一个 1,再次把这个 1 移动给 key2,此时 smove 不会视为出错,也会按照 删除-插入 进行执行。

  • srem:将指定的元素从 set 中删除
  • 语法:srem key member [member ...]
  • 时间复杂度:O(N),N 是要删除的元素个数。
  • 返回值:删除成功的元素个数。

不同操作的返回值,含义差别还是挺大的,需要用的时候,多翻文档即可。

集合间操作

  • 交集 (inter):最终结果同时出现在两个集合中。
  • 并集 (union):把多个集合中的数据都集中放在一起,如果元素有重复,也最终只保留一份。
  • 差集 (diff):A 和 B 做差集,就是找出 A 中存在,但是 B 中不存在的元素。

交集:sinter、sinterstore

  • sinter:获取给定 set 的交集中的元素。
  • 语法:sinter key [key ...]
  • 时间复杂度:O(N * M),N 是最小的集合元素个数,M 是最大的集合元素个数。
  • 返回值:交集的元素。
  • sinterstore:获取给定 set 的交集中的元素并保存到目标 set 中。
  • 语法:sinterstore destination key [key ...]
  • 时间复杂度:O(N * M),N 是最小的集合元素个数,M 是最大的集合元素个数。
  • 返回值:交集的元素个数。

并集:sunion、sunionstore

  • sunion:获取给定 set 的并集中的元素。
  • 语法:sunion key [key ...]
  • 时间复杂度:O(N),N 是给定的所有集合的总的元素个数。
  • 返回值:并集的元素。
  • sunionstore:获取给定 set 的并集中的元素并保存到目标 set 中。
  • 语法:sunionstore destination key [key ...]
  • 时间复杂度:O(N),N 是给定的所有集合的总的元素个数。
  • 返回值:并集的元素个数。

差集:sdiff、sdiffstore

  • sdiff:获取给定 set 的差集中的元素。
  • 语法:sdiff key [key ...]
  • 时间复杂度:O(N),N 是给定的所有集合的总的元素个数。
  • 返回值:差集的元素。
  • sdiffstore:获取给定 set 的差集中的元素并保存到目标 set 中。
  • 语法:sdiffstore destination key [key ...]
  • 时间复杂度:O(N),N 是给定的所有集合的总的元素个数。
  • 返回值:差集的元素个数。

三. set 命令小结

命令 执行效果 时间复杂度
sadd key member member ... 向集合中添加元素 O(K),K 是元素的个数
smembers key 求集合中的元素 O(K),K 是元素的个数
sismember key member 判断元素是否在集合中 O(1)
scard key 获取集合中元素的个数 O(1)
spop key count 随机删除 count 个元素 O(N),N 是 count
srandmember key count 随机删除 count 个元素 O(N),N 是 count
smove source destination member 移动源集合中的一个元素到目标集合 O(1)
srem key member member ... 删除集合中的元素 O(K),K 是元素的个数
sinter key key ... 获取集合的交集 O(N * M),N 是最小的集合元素个数,M 是最大的集合元素个数
sinterstore destination key key ... 存储集合的交集到目标集合中 O(N * M),N 是最小的集合元素个数,M 是最大的集合元素个数
sunion key key ... 获取集合的并集 O(N),N 是所有集合的元素个数
sunionstore destination key key ... 存储集合的并集到目标集合中 O(N),N 是所有集合的元素个数
sdiff key key ... 获取集合的差集 O(N),N 是所有集合的元素个数
sdiffstore destination key key ... 存储集合的差集到目标集合中 O(N),N 是所有集合的元素个数

四. set 内部编码方式

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

  • intset (整数集合):当集合中的元素都是整数并且元素的个数小于 set-max-intset-entries 配置 (默认 512 个) 时,Redis 会选用 intset 来作为集合的内部实现,从而减少内存的使用。
    • Redis 是内存数据库,当元素均为整数,并且元素的个数不是很多的时候,为了节省空间,做出的特点优化。
  • hashtable (哈希表):当集合类型无法满足 intset 的条件时,Redis 会使用 hashtable 作为集合的内部实现。

使用 object encoding key 可以查看集合内部的编码方式,如下:

五. set 使用场景

用户标签

集合类型比较典型的使用场景是标签 (tag),使用 set 来保存用户的 "标签"

  • 例如 A 用户对娱乐、体育板块比较感兴趣,B 用户对历史、新闻比较感兴趣,这些兴趣点可以被抽象为标签。例如一个电子商务网站会对不同标签的用户做不同的产品推荐。
  • 有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于增强用户体验和用户黏度都非常有帮助。分析出你这个人的一些特征,分析清楚之后,再投其所好。
  • 上述用户数据,很多公司都在共享。
    • 两个程序,两个账号,如何知道这两个账号是一个人?
    • 现在的程序登入,主要就是两个入口,手机号、微信。
  • 通过上述过程,搜集到的用户特征,就会转换成 "标签" (简短的字符串),此时就可以把标签保存到 Redis 的 set 中。
    • 用户画像,这种事情其实是挺复杂的事情,一般一个大厂都会有专门的团队做这样工作。
    • 上述玩法,抖音玩的是最好的,其它互联网大厂一看这么搞真好,于是纷纷效仿。
    • 但是存在 "信息茧房" 问题:你看到的内容始终就是一个小圈子,看不到其它圈子中的事物。
    • 当你很认真的看了一个视频之后,人家的服务器就判定你,非常爱看这种类型的视频,接下来就给你疯狂推送。

下面的演示通过集合类型来实现标签的若干功能。

  • 给用户添加标签
cpp 复制代码
sadd user:1:tags tag1 tag2 tag5
sadd user:2:tags tag2 tag3 tag5
...
sadd user:k:tags tag1 tag2 tag4
  • 给标签添加用户
cpp 复制代码
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
  • 删除用户下的标签
cpp 复制代码
srem user:1:tags tag1 tag5
...
  • 删除标签下的用户
cpp 复制代码
srem tag1:users user:1
srem tag5:users user:1
...
  • 计算用户的共同兴趣标签
cpp 复制代码
sinter user:1:tags user:2:tags

共同好友

使用 set 来计算用户之间的公共好友,基于 "集合求交集"

  • 例如:QQ,我这边加了很多好友,你那边也加了很多好友。
  • 基于上述还可以做一些好友推荐:A 和 B 是好友,A 和 C 是好友,B 和 C 和 D 都是好友,此时系统就会把 D 推荐给 A

统计 UV

一个互联网产品,如何衡量用户量,用户规模?主要的指标,是两方面:

  • PV (page view):用户每次访问该服务器,都会产生一个 PV
  • UV (user view):每个用户访问该服务器,都会产生一个 UV,但是同一个用户多次访问,不会增加 UV 的个数。
    • UV 需要按照用户进行去重,上述的去重功能,就可以使用 set 来实现。
相关推荐
●VON9 小时前
鸿蒙Flutter实战:分类管理页BottomSheet CRUD
数据库·flutter·华为·harmonyos·鸿蒙
Cosolar9 小时前
Chroma向量库面试学习指南
数据库·人工智能·面试·职场和发展·数据库架构
方也_arkling10 小时前
【Java-Day08】static / final / 枚举
java·开发语言
橙淮10 小时前
Spring Bean作用域与生命周期全解析
java·spring
Chengbei1110 小时前
一站式源码安全检测工具、云安全 / APP / 小程序源码敏感信息递归多层目录扫描AK、JWT、手机号、身份证等敏感信息
java·开发语言·安全·web安全·网络安全·系统安全·安全架构
llz_11210 小时前
web-第一次课后作业
java·开发语言·idea
企服AI产品测评局10 小时前
Agent适配信创环境实测:企业级自动化如何实现国产操作系统与数据库全兼容?
运维·数据库·人工智能·ai·chatgpt·自动化
秋911 小时前
Java项目运行5天左右自动宕机:系统性定位与解决方案
java·开发语言·python
小江的记录本11 小时前
【JVM虚拟机】垃圾回收GC:垃圾收集器:CMS:核心原理、回收流程、优缺点、废弃原因(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·spring·面试·maven
cfm_291411 小时前
Redis数据安全性解析
数据库·redis·缓存