一、前言:Set ------ Redis 中的"数学集合"
在 Redis 的五大数据类型中,Set(集合) 是最贴近数学概念的一种:
- ✅ 元素唯一(自动去重)
- ✅ 无序(不保证插入顺序)
- ✅ 支持交集、并集、差集等集合运算
它不仅是去重工具,更是实现:
- 共同好友推荐
- 用户标签系统
- 抽奖去重
- 权限管理
的核心数据结构!
本文将带你:
✅ 掌握 Set 的核心命令
✅ 理解其底层实现(intset / hashtable)
✅ 结合真实业务场景
✅ 避开常见使用误区
二、Set 类型基本特性
- 元素唯一性:重复添加无效
- 无序性 :
SMEMBERS返回顺序不可预测 - 支持集合运算:交(SINTER)、并(SUNION)、差(SDIFF)
- 底层编码 :
- intset:所有元素为整数且数量少(默认 ≤ 512)
- hashtable:包含字符串或元素较多时
- 时间复杂度 :
SADD/SISMEMBER/SREM均为 O(1)
三、核心命令详解(附实战示例)
1. SADD key member [member ...] ------ 添加元素
bash
# 添加用户标签
SADD user:1001:tags "科技" "篮球" "电影"
# 重复添加无效
SADD user:1001:tags "科技"
# 返回 0(未新增)
✅ 返回值:成功添加的元素个数
2. SMEMBERS key ------ 获取所有元素
bash
SMEMBERS user:1001:tags
# 返回 1) "电影" 2) "篮球" 3) "科技" (顺序随机)
⚠️ 慎用 :当 Set 很大时(如百万级),会阻塞 Redis!
✅ 替代方案:用
SSCAN分批遍历
3. SISMEMBER key member ------ 判断元素是否存在
bash
SISMEMBER user:1001:tags "足球"
# 返回 0(不存在)
SISMEMBER user:1001:tags "篮球"
# 返回 1(存在)
✅ 典型用途:
- 判断用户是否已参与活动
- 检查权限是否包含某角色
4. SREM key member [member ...] ------ 删除元素
bash
# 移除标签
SREM user:1001:tags "电影" "游戏"
💡 不会删除整个 Key,除非所有元素都被移除
5. SCARD key ------ 获取集合大小
bash
SCARD user:1001:tags
# 返回 3
✅ O(1) 操作,高效可靠
6. SRANDMEMBER key [count] ------ 随机获取元素
bash
# 随机返回 1 个标签
SRANDMEMBER user:1001:tags
# 随机返回 2 个不重复标签
SRANDMEMBER user:1001:tags 2
# 随机返回 3 个可重复标签(带负数)
SRANDMEMBER user:1001:tags -3
🎯 应用场景:
- 抽奖系统(去重随机)
- 推荐系统冷启动(随机曝光)
7. 集合运算命令(多 Key 操作)
| 命令 | 作用 | 示例 |
|---|---|---|
SINTER key1 key2 ... |
交集 | 共同好友 |
SUNION key1 key2 ... |
并集 | 合并标签 |
SDIFF key1 key2 |
差集(key1 - key2) | A 关注但 B 未关注的人 |
bash
# 用户 A 和 B 的共同兴趣
SINTER user:1001:tags user:1002:tags
# 返回 1) "科技" 2) "篮球"
# A 有但 B 没有的标签
SDIFF user:1001:tags user:1002:tags
⚡ 所有集合运算均为原子操作,并发安全!
四、Set 的典型应用场景
场景 1:用户标签系统
bash
SADD user:1001:tags "男性" "25-30岁" "一线城市" "程序员"
SADD user:1002:tags "女性" "20-25岁" "二线城市" "设计师"
# 精准推送:找"程序员"标签用户
SISMEMBER user:1001:tags "程序员" → true
✅ 优势:标签增删快,查询 O(1)
场景 2:共同好友/关注推荐
bash
# 用户关注列表
SADD follows:1001 2001 2002 2003
SADD follows:1002 2002 2003 2004
# 共同关注
SINTER follows:1001 follows:1002
# 返回 2002, 2003
💡 社交网络的核心功能之一
场景 3:抽奖去重
bash
# 用户参与抽奖
SADD lottery:20241103 user_1001
SADD lottery:20241103 user_1002
# 防止重复参与
SISMEMBER lottery:20241103 user_1001 → true(已参与)
# 随机抽取 3 名幸运用户
SRANDMEMBER lottery:20241103 3
🔒 天然去重,无需额外校验
场景 4:IP 黑名单/白名单
bash
SADD ip:blacklist "192.168.1.100" "10.0.0.5"
# 请求时检查
SISMEMBER ip:blacklist $client_ip
✅ 比数据库查询快几个数量级
五、Set vs List 对比
| 特性 | Set | List |
|---|---|---|
| 元素唯一 | ✅ 自动去重 | ❌ 允许重复 |
| 有序性 | ❌ 无序 | ✅ 有序 |
| 随机访问 | ❌ 不支持 | ✅ 支持(但慢) |
| 集合运算 | ✅ 交/并/差 | ❌ 不支持 |
| 适用场景 | 去重、关系计算 | 队列、最新列表 |
📌 选型建议:
- 需要去重或做关系计算 → Set
- 需要保持顺序或实现队列 → List
六、常见误区与最佳实践
❌ 误区 1:用 SMEMBERS 遍历大 Set
风险 :O(N) 操作,导致 Redis 卡顿
替代 :使用SSCAN分批读取
bash
SSCAN user:big_set 0 MATCH * COUNT 100
❌ 误区 2:存储超大 Set(如千万级用户 ID)
问题 :单 Key 内存占用高,影响性能
建议 :分片存储(如user:tag:tech:shard1,shard2...)
✅ 最佳实践:
- Key 命名规范 :
业务:实体:属性(如user:1001:tags) - 避免 Big Set:单 Set 元素 ≤ 10,000
- 集合运算前评估数据量:两个百万级 Set 求交集可能耗时秒级
- 用
SISMEMBER代替SMEMBERS+ 遍历:判断存在性更高效
七、底层实现:intset 与 hashtable 的自动切换
Redis 根据以下条件自动选择编码:
- 所有元素为整数
- 元素数量 ≤
set-max-intset-entries(默认 512)
满足则用 intset (内存紧凑,整数排序存储),否则用 hashtable。
💡 可通过
OBJECT ENCODING myset查看当前编码。
八、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!