玩转 Redis · Set 篇

0 阅读7分钟

玩转 Redis · Set 篇

环境:Docker 容器 redis-learn(Redis 7.4.11) 本篇地位:五大类型 4/5。一句话:自动去重、无序的集合


0. 一分钟回顾

Set 可以理解为一个“自动去重的袋子”:往里面放相同的元素,Redis 只保留一份;但它不保证元素的顺序。

user:1:tags → { redis, database }

Set 不是 List:

  • List 有顺序,可以按下标访问;Set 无序,不能依赖返回顺序。
  • List 允许重复元素;Set 自动去重。
  • List 擅长两端进出;Set 擅长成员判断和集合运算。

典型场景:用户标签、去重、共同好友、共同兴趣、权限集合、集合差异比较。


1. 命令全家桶

操作命令一句话说明
SADD key member [member …]添加成员,重复成员自动忽略;返回实际新增数量
SMEMBERS key查看全部成员;顺序不固定
SISMEMBER key member判断成员是否存在;返回 1/0
SCARD key查看成员数量
SREM key member [member …]按值删除成员;返回实际删除数量
SINTER key [key …]求交集:多个集合共同拥有的成员
SUNION key [key …]求并集:多个集合全部成员去重合并
SDIFF key [key …]求差集:第一个集合有、后续集合没有
SRANDMEMBER key [count]随机查看成员,不删除
SPOP key [count]随机取出并删除成员
SMOVE source destination member把成员从一个 Set 移动到另一个 Set
DEL key删除整个 Set(通用命令)

2. 返回值与核心规律

  • SADD 返回实际新增成员数量,不是传入参数数量。
  • SISMEMBER 返回 1 表示存在,0 表示不存在。
  • SCARD 返回 Set 成员数量,类似 List 的 LLEN
  • SREM 返回实际删除成员数量。
  • SMOVE 返回 1 表示移动成功,0 表示源集合中没有该成员。
  • SPOP 返回被随机取出的成员;如果集合空了,最后一个成员删除后 key 自动消失。
  • SMEMBERSSINTERSUNIONSDIFF 的返回顺序都不应作为业务依据。
  • key 必须完全一致:user:2:tagsuser:2:tags: 是两个不同的 key。

3. 实操流水(真实命令 + 结果)

3.1 添加成员与自动去重:SADD

127.0.0.1:6379> SADD user:1:tags redis
1
127.0.0.1:6379> SADD user:1:tags redis "database"
1

第二次传入了两个成员,但 redis 已经存在,只有 database 是新增成员,所以返回 1

3.2 查看全部成员:SMEMBERS

127.0.0.1:6379> SMEMBERS user:1:tags
redis
database

集合当前有 redisdatabase。Set 无序,返回顺序不固定,不能依赖本次顺序。

3.3 判断成员与统计数量:SISMEMBER、SCARD

127.0.0.1:6379> SISMEMBER user:1:tags database
1
127.0.0.1:6379> SISMEMBER user:1:tags "docker"
0
127.0.0.1:6379> SCARD user:1:tags
2

database 存在,返回 1docker 不存在,返回 0;集合共有两个成员。

3.4 按值删除:SREM

127.0.0.1:6379> SREM user:1:tags "database"
1
127.0.0.1:6379> SMEMBERS user:1:tags
redis

SREM 返回 1,表示成功删除 database

3.5 集合运算:交集、并集、差集

重新创建第二个用户的标签集合:

127.0.0.1:6379> SADD user:2:tags "database" "linux" "redis"
3

求两个用户共同拥有的标签:

127.0.0.1:6379> SINTER user:1:tags user:2:tags
redis

求两个集合全部不重复成员:

127.0.0.1:6379> SUNION user:1:tags user:2:tags
redis
database
linux

差集的方向很重要。第一次查询用户 1 独有的标签:

127.0.0.1:6379> SDIFF user:1:tags user:2:tags

没有输出,表示用户 1 有、用户 2 没有的标签不存在。

反过来查询用户 2 独有的标签:

127.0.0.1:6379> SDIFF user:2:tags user:1:tags
database
linux

SDIFF A B 的含义是:A 有、B 没有。

3.6 随机查看与随机取出:SRANDMEMBER、SPOP

随机查看成员:

127.0.0.1:6379> SRANDMEMBER user:2:tags
redis
127.0.0.1:6379> SRANDMEMBER user:2:tags
redis
127.0.0.1:6379> SRANDMEMBER user:2:tags
linux

结果可以重复,因为 SRANDMEMBER 只查看,不删除成员。

之后使用 SPOP 随机取出并删除成员:

127.0.0.1:6379> SPOP user:2:tags
database
127.0.0.1:6379> SMEMBERS user:2:tags
redis
127.0.0.1:6379> SPOP user:2:tags
redis
127.0.0.1:6379> SMEMBERS user:2:tags

第二次 SMEMBERS 没有输出,说明最后一个成员也被删除。

验证 key:

127.0.0.1:6379> EXISTS user:2:tags
0

3.7 移动成员:SMOVE

重新准备两个集合:

127.0.0.1:6379> SADD user:1:tags "redis" "docker"
1

这里 redis 原本已经存在,所以只新增了 docker

练习中曾误写了带末尾冒号的 key:

127.0.0.1:6379> SADD user:2:tags: "docker"
1
127.0.0.1:6379> SADD user:2:tags: "linux"
1

user:2:tags: 与正确的 user:2:tags 不同,后来在正确 key 上补入 linux

127.0.0.1:6379> SADD user:2:tags "linux"
1

docker 从用户 1 移到用户 2:

127.0.0.1:6379> SMOVE user:1:tags user:2:tags "docker"
1
127.0.0.1:6379> SMEMBERS user:2:tags
linux
docker
127.0.0.1:6379> SMEMBERS user:1:tags
redis

SMOVE 是真正的移动,不是复制:源集合中的 docker 已消失,目标集合中出现了 docker

清理误创建的临时 key:

127.0.0.1:6379> DEL user:2:tags:
1
127.0.0.1:6379> EXISTS user:2:tags:
0

这里最后的 EXISTS user:2:tags 返回 0,验证的是误创建的带末尾冒号的 key 已清理;正确的 user:2:tags 仍然存在。


4. 踩坑实录

现象教训
把 Set 当有序数组以为 SMEMBERS 每次顺序固定Set 无序,不能依赖成员返回顺序
重复添加成员SADD 传入两个值但返回 1返回的是实际新增数量,重复成员会被忽略
差集方向弄反SDIFF user:1:tags user:2:tags 没结果SDIFF A B 是 A 有、B 没有
混淆 SRANDMEMBERSPOP以为随机查看会删除SRANDMEMBER 只看;SPOP 取出并删
key 末尾多冒号user:2:tags: 查询不到 user:2:tags 的内容冒号也是 key 的一部分,拼写必须完全一致
误看空集合SMEMBERS 没输出可能是 key 不存在或集合为空,用 EXISTS 继续确认

5. 典型场景

5.1 用户标签去重

SADD user:1:tags redis docker database
SISMEMBER user:1:tags redis
SMEMBERS user:1:tags

同一个标签多次添加也只保留一份。

5.2 共同兴趣或共同好友

SINTER user:1:tags user:2:tags

交集就是两个人共同拥有的标签或共同好友。

5.3 权限或功能集合差异

SDIFF user:2:permissions user:1:permissions

可以找出一个用户多出来的权限。

5.4 随机抽奖

SPOP lottery:participants

抽出成员并删除,适合“不重复中奖”的简单抽奖。若只想随机看看,不改变候选集合,使用 SRANDMEMBER


6. 核心记忆

Set 是自动去重、无序的集合,适合标签、去重、共同好友和集合运算。

SADD          加成员并自动去重
SMEMBERS      看全部成员
SISMEMBER     判断成员是否存在
SCARD         看成员数量
SREM          按值删除
SINTER        求共同成员
SUNION        合并全部不重复成员
SDIFF         求第一个集合独有成员
SRANDMEMBER   随机看,不删除
SPOP          随机取出并删除
SMOVE         在两个集合之间移动

记忆画面:

Set = 一个不允许重复、没有固定顺序的集合袋子

7. 自检清单

  • Set 和 List 在顺序、重复元素方面有什么区别?
  • SADD 返回的是传入数量还是实际新增数量?
  • SMEMBERS 的返回顺序能不能依赖?
  • SISMEMBER 返回 10 分别代表什么?
  • SCARD 统计什么?
  • SREMSPOP 的删除方式有什么区别?
  • SRANDMEMBER 会不会删除成员?
  • SINTERSUNIONSDIFF 分别是什么?
  • SDIFF A B 的方向如何理解?
  • SMOVE 是复制还是移动?
  • user:2:tagsuser:2:tags: 是同一个 key 吗?
  • Set 最后一个成员被删除后,key 会怎样?

8. 类型地图(4/5)

篇章形状典型用途招牌命令状态
📗 String一行字的纸条缓存、计数器SET GET INCR
📘 Hash小表格(字段↔值)对象、单字段修改、购物车HSET HGET HINCRBY
📙 List有顺序的双向链表消息队列、最新列表LPUSH RPOP LRANGE
📕 Set自动去重的无序集合标签、去重、共同好友SADD SINTER SMEMBERS
📓 ZSet带分数自动排队排行榜、优先级ZADD ZRANGE⬅️ 下一站