一、前言:Set 不是"集合",而是高效工具箱
Redis 的 Set(集合) 是一个无序、唯一、基于哈希表 的数据结构,天然支持去重 和集合运算。
典型应用场景包括:
- ✅ 用户标签系统(如"科技"、"金融")
- ✅ 社交关系(好友、关注、粉丝)
- ✅ 抽奖去重(已中奖用户不再参与)
- ✅ 共同兴趣推荐(交集计算)
本文将系统讲解 Redis Set 的核心命令 ,并通过真实业务案例展示其强大能力。
二、Set 核心命令速查表
| 命令 | 作用 | 时间复杂度 |
|---|---|---|
SADD key member [member ...] |
添加一个或多个成员 | O(1) 每个成员 |
SREM key member [member ...] |
移除一个或多个成员 | O(1) 每个成员 |
SMEMBERS key |
获取所有成员 | O(N),N=成员数 |
SCARD key |
获取集合大小 | O(1) |
SISMEMBER key member |
判断成员是否存在 | O(1) |
SRANDMEMBER key [count] |
随机返回一个或多个成员 | O(N),N=count |
SPOP key [count] |
随机弹出并移除成员 | O(N) |
SMOVE source destination member |
将成员从一个集合移到另一个 | O(1) |
集合运算命令
| 命令 | 作用 | 时间复杂度 |
|---|---|---|
SINTER key [key ...] |
求多个集合的交集 | O(N×M),N=最小集合大小,M=集合数 |
SUNION key [key ...] |
求多个集合的并集 | O(N+M+...) |
SDIFF key [key ...] |
求第一个集合与其他集合的差集 | O(N+M+...) |
SINTERSTORE dest key [key ...] |
将交集结果存储到新集合 | 同 SINTER |
SUNIONSTORE dest key [key ...] |
将并集结果存储 | 同 SUNION |
SDIFFSTORE dest key [key ...] |
将差集结果存储 | 同 SDIFF |
💡 关键特性:
- 成员唯一(自动去重)
- 无序(不保证插入顺序)
- 支持高效集合运算
三、常用命令详解与示例
3.1 基础操作:添加、查询、删除
bash
# 添加标签(自动去重)
127.0.0.1:6379> SADD user:1001:tags "tech" "finance" "ai"
(integer) 3
# 再次添加重复标签
127.0.0.1:6379> SADD user:1001:tags "tech" "blockchain"
(integer) 1 # 只新增了 "blockchain"
# 查询所有标签
127.0.0.1:6379> SMEMBERS user:1001:tags
1) "ai"
2) "blockchain"
3) "finance"
4) "tech"
# 判断是否包含某标签
127.0.0.1:6379> SISMEMBER user:1001:tags "ai"
(integer) 1
# 获取标签数量
127.0.0.1:6379> SCARD user:1001:tags
(integer) 4
3.2 随机操作:SRANDMEMBER / SPOP
bash
# 随机抽取 2 个标签(不移除)
127.0.0.1:6379> SRANDMEMBER user:1001:tags 2
1) "finance"
2) "tech"
# 随机弹出 1 个标签(移除)
127.0.0.1:6379> SPOP user:1001:tags
"ai"
✅ 典型场景:抽奖、随机推荐、A/B 测试分组
3.3 集合运算:交集、并集、差集
bash
# 用户 A 的兴趣
SADD user:A:interests "music" "movie" "game"
# 用户 B 的兴趣
SADD user:B:interests "movie" "sports" "game"
# 共同兴趣(交集)
127.0.0.1:6379> SINTER user:A:interests user:B:interests
1) "game"
2) "movie"
# 所有兴趣(并集)
127.0.0.1:6379> SUNION user:A:interests user:B:interests
1) "game"
2) "movie"
3) "music"
4) "sports"
# A 有但 B 没有的兴趣(差集)
127.0.0.1:6379> SDIFF user:A:interests user:B:interests
1) "music"
🔥 这是实现"可能认识的人"、"猜你喜欢"的核心逻辑!
四、Set 的内部编码优化
Redis 对小 Set 使用 intset(整数集合) 或 hashtable(哈希表) 编码:
# redis.conf 默认配置
set-max-intset-entries 512 # 成员全为整数且 ≤512 时用 intset
- intset:内存更省,仅支持整数
- hashtable:通用,支持字符串
💡 建议:
- 小集合(如标签、好友)性能极佳
- 避免存储超大 Set(如 > 100 万元素)
五、实战应用场景
场景 1:用户标签系统
java
// Java (Lettuce)
String userId = "1001";
String tagKey = "user:" + userId + ":tags";
// 添加标签
redis.sadd(tagKey, "tech", "ai");
// 判断用户是否属于"科技"人群
Boolean isTechUser = redis.sismember(tagKey, "tech");
// 获取所有带"ai"标签的用户(需反向索引,见下文)
📌 扩展 :如需"反查",可建立 tag → users 的反向 Set:
bashSADD tag:ai user:1001 user:1002
场景 2:社交关系(好友/关注)
python
# Python (redis-py)
# 用户 1001 关注了 2001, 2002
r.sadd("follow:1001", "2001", "2002")
# 用户 2001 的粉丝
r.sadd("fans:2001", "1001")
# 判断是否关注
r.sismember("follow:1001", "2001") # True
# 共同关注(我关注的人中,也关注了 X 的人)
r.sinter("follow:1001", "fans:2002")
✅ 优势:O(1) 判断关系,O(N) 计算交集
场景 3:抽奖去重
bash
# 已中奖用户
SADD lottery:winners user:1001 user:1002
# 新用户参与抽奖前检查
SISMEMBER lottery:winners user:1003 # 返回 0 → 可参与
# 中奖后加入
SADD lottery:winners user:1003
六、常见误区与最佳实践
❌ 误区 1:用 SMEMBERS 遍历大集合
- 问题:阻塞主线程,网络爆炸
- 建议 :使用
SSCAN游标分批遍历(适用于 > 1000 元素)
bash
127.0.0.1:6379> SSCAN big_set 0 COUNT 100
❌ 误区 2:频繁执行多 Key 集合运算
- 问题 :
SINTER key1 key2 key3若 key 很大,会阻塞 - 建议 :
- 限制参与运算的集合大小
- 使用
SINTERSTORE异步生成结果集
✅ 最佳实践
- 命名规范 :如
tag:ai、follow:user_id - 设置 TTL:临时集合(如抽奖)加过期时间
- 监控大小 :通过
SCARD+ 告警防止膨胀 - 避免大 Value:成员建议 < 64 字节
七、Set vs List vs Sorted Set 选型建议
| 需求 | 推荐类型 |
|---|---|
| 去重 + 无序 | Set |
| 有序列表 + 范围查询 | Sorted Set |
| 队列/栈/时间线 | List |
| 对象属性存储 | Hash |
📌 Set 的核心价值:唯一性 + 集合运算
八、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!