首页 / Redis 入门教程 / 集合 Set

Redis 入门教程

集合 Set

本教程共 40 篇 · 第 9 篇 · 更新于 2026-08-02

redisset集合SADDSINTER共同好友去重

9. 集合 Set

本节目标

  • 理解 Set 是「无序 + 成员唯一」的 String 集合,增删查复杂度 O(1)
  • 掌握写入/读取/判断成员:SADD / SMEMBERS / SISMEMBER / SCARD / SREM
  • 掌握集合运算:交集 SINTER、并集 SUNION、差集 SDIFF 及其 STORE 变体
  • 用共同好友、标签去重、随机抽奖等场景理解 Set 的用武之地
  • 了解用 SSCAN 替代 SMEMBERS 避免大集合阻塞

Set(集合)是 Redis 中一组无序且成员唯一的 String 集合。所谓「唯一」,是指同一个成员(member)在集合里最多出现一次;你重复 SADD 同一个值,第二次会被忽略,返回 0。所谓「无序」,是指集合不保证遍历顺序,每次 SMEMBERS 返回的顺序都不应被依赖(底层由哈希表或整数集维护)。

和集合相关的几个事实:

  • 底层编码可能是 intset(成员全是整数且数量较少时)或 hashtable,Redis 自动选择,对使用者完全透明。
  • 单个集合最多可容纳 2^32 - 1(约 42 亿)个成员。
  • 添加、删除、查找成员的复杂度都是 O(1)。

9-1 写入与读取

最基础的写入命令是 SADD,可以一次添加一个或多个成员:

命令:

127.0.0.1:6379> SADD tags:article:100 "redis" "database" "cache"

输出:

(integer) 3

返回值表示「本次加入的成员数量」。如果某个成员已存在,它不会被重复计入。我们再尝试加入一个已存在和一个新的:

命令:

127.0.0.1:6379> SADD tags:article:100 "redis" "nosql"

输出:

(integer) 1

redis 已经存在,只有 nosql 是新成员,所以返回 1。

读取全部成员用 SMEMBERS

命令:

127.0.0.1:6379> SMEMBERS tags:article:100

输出:

1) "redis"
2) "database"
3) "cache"
4) "nosql"

判断某个成员是否在集合中用 SISMEMBER,获取成员数量用 SCARD

命令:

127.0.0.1:6379> SISMEMBER tags:article:100 "redis"
127.0.0.1:6379> SCARD tags:article:100

输出:

(integer) 1
(integer) 4

SISMEMBER 返回 1 表示存在、0 表示不存在,比先 SMEMBERS 再在客户端遍历要高效得多(始终 O(1))。

删除成员用 SREM,随机弹出一个成员用 SPOP,随机取若干个但不删除用 SRANDMEMBER

命令:

127.0.0.1:6379> SREM tags:article:100 "cache"
127.0.0.1:6379> SRANDMEMBER tags:article:100 2
127.0.0.1:6379> SPOP tags:article:100

输出:

(integer) 1
1) "redis"
2) "nosql"
"database"

9-2 集合运算:交集、并集、差集

Set 最强大的能力是集合间的数学运算,这正是「关系型数据里要用 JOIN 才能算的活」在 Redis 里一条命令搞定。

假设有两个用户的好友集合:

命令:

127.0.0.1:6379> SADD friends:alice "bob" "carol" "dave"
127.0.0.1:6379> SADD friends:bob "alice" "carol" "erin"

输出:

(integer) 3
(integer) 3

交集 SINTER——两人共同的好友:

命令:

127.0.0.1:6379> SINTER friends:alice friends:bob

输出:

1) "carol"

并集 SUNION——两人合计认识的所有人:

命令:

127.0.0.1:6379> SUNION friends:alice friends:bob

输出:

1) "bob"
2) "carol"
3) "dave"
4) "alice"
5) "erin"

差集 SDIFF——Alice 认识但 Bob 不认识的人(顺序敏感,A 减 B):

命令:

127.0.0.1:6379> SDIFF friends:alice friends:bob

输出:

1) "bob"
2) "dave"

这些运算都有对应的 STORE 变体(SINTERSTORE / SUNIONSTORE / SDIFFSTORE),把结果直接存成一个新 key,便于缓存或后续复用:

命令:

127.0.0.1:6379> SINTERSTORE mutual:alice:bob friends:alice friends:bob

输出:

(integer) 1

提示:当集合很大时,SMEMBERS 会一次性返回全部成员,可能阻塞服务器。生产环境遍历大集合请用 SSCAN(用法与 SCAN 一致,带游标分批返回),第 04 章已介绍 SCAN 思路。Redis 8.x 在集合运算的执行效率上持续做了优化,但对超大集合求交集仍要注意耗时。

9-3 实战场景

场景一:共同好友 / 共同关注。 如上面例子,把每个用户的关系存成 Set,求交集即得「你可能认识的人」「共同好友」。微博、抖音的「共同关注」就是这类运算。

场景二:标签与去重。 给文章、商品打标签,把标签存成 Set,天然去重;想找「同时带 redis 和 cache 标签的文章」,对多篇文章的标签集合求交集即可。

场景三:随机抽奖 / 抽签。 SPOP 随机且不放回地弹出成员,非常适合「从奖池抽 N 个中奖者」;SRANDMEMBER 则是「抽但先不移除」,适合「随机推荐」「随机展示」。

场景四:轻量独立名单。 既要去重、又想随时列出具体成员(例如「本日访问过该页面的用户名单」),用 Set 存用户 ID 再 SCARD 取人数最直接;若只关心「大约有多少人」而不需要名单,第 12 章的 HyperLogLog 更省内存。

9-4 常见误区

  • 误区:集合有序。 Set 是无序的,SMEMBERS 的顺序不可依赖;若需要排序或带分数的集合,请看下一章「有序集合 Sorted Set」。
  • 误区:SADD 重复添加会报错。 不会报错,重复成员被忽略,返回值只统计新加入的数量。
  • 误区:SINTER 比在应用层循环判断快不了多少。 集合运算在服务端一次性完成,避免了把大量数据拉到应用层再比较的网络与计算开销,差距是数量级的。
  • 误区:SMEMBERS 能安全遍历百万级集合。 它会一次性返回全部成员(O(N)),大集合请改用 SSCAN

9.x Set 与其他类型的边界

初学者常混淆 Set、List 与 Sorted Set,记住一句口诀即可:List 是「有序可重复」的队列,Set 是「无序且去重」的集合,Sorted Set 是「按分数排序且去重」的集合。例如「用户最近浏览记录」允许重复、强调顺序,用 List;「用户收藏夹」不重复,用 Set;「商品热度榜」需要排序,用 Sorted Set。

Set 的成员只能是 String,不能嵌套结构。虽然单个集合容量极大(约 42 亿成员),但 SINTER / SUNION / SDIFF 的耗时与参与集合的总成员数相关。当集合非常庞大时,建议采取三条措施:一、尽量在从库执行集合运算,避免影响主库响应;二、用 *STORE 把运算结果缓存成一个新 key,供后续反复读取,避免每次都重算;三、对超大集合先做采样或按业务分片,缩小单次运算规模。Redis 8.x 对这些集合运算做了持续的内部优化,但容量规划仍要把大集合运算的成本考虑进去,不能假设它永远是 O(1)。

此外,Set 非常适合做「关系型数据里要用 SQL 的 JOIN + DISTINCT 才能算的活」:共同好友、共同标签、差集推荐,本质都是集合运算。把关系存成 Set 后,一条 SINTER 就取代了应用层把两份大列表拉回来循环比对的笨办法,网络与计算开销都小得多。

9-5 小结

  • Set 是无序、成员唯一的 String 集合,增删查 O(1),最大约 42 亿成员。
  • 写入/读取:SADD / SMEMBERS / SISMEMBER / SCARD / SREM
  • 随机:SPOP(弹出) / SRANDMEMBER(不弹出)。
  • 集合运算:交集 SINTER、并集 SUNION、差集 SDIFF,及对应的 *STORE 持久化变体。
  • 用武之地:共同好友、标签去重、随机抽奖、轻量独立名单。
  • 大集合遍历用 SSCAN 而非 SMEMBERS

提示:关于版本——本教程正文统一以 Redis 8.x(最新稳定版)为准;官方 redis.io 下载页另标 8.8 为 “Latest stable”,而 GitHub 上 redis/redis 的最新发布 tag 为 8.10.0,二者同属 8.x,命令与类型差异对教材影响极小。