Redis Set 类型
1. Set 类型概述
Section titled “1. Set 类型概述”Set 是 Redis 中无序、不重复的字符串集合,自动对插入的重复元素去重,并支持集合间的交集、并集、差集运算。
Set 结构示意:
key = "user:1001:follow" key = "user:1002:follow"┌─────────────────────┐ ┌─────────────────────┐│ Alice Bob Carol │ SINTER │ Bob Carol Dave ││ Dave Eve │ ────────▶ │ │└─────────────────────┘ └─────────────────────┘ 结果:{Bob, Carol}(共同关注)| 特点 | 说明 |
|---|---|
| 不重复 | 自动去重,插入重复元素会被忽略 |
| 无序 | 元素没有固定顺序,不支持按索引访问 |
| 集合运算 | 原生支持交集、并集、差集 |
| 随机访问 | SRANDMEMBER 随机取元素,适合抽奖 |
| 上限 | 单个 Set 最多 2^32 - 1 个元素 |
2. 常用命令
Section titled “2. 常用命令”2.1. 基础操作
Section titled “2.1. 基础操作”# 添加SADD tags "Java" "Redis" "Spring" # 添加元素,返回成功添加的数量SADD tags "Java" # 重复元素,返回 0(自动忽略)
# 删除SREM tags "Spring" # 删除指定元素
# 查询SMEMBERS tags # 获取所有元素(无序)SCARD tags # 获取元素数量SISMEMBER tags "Redis" # 判断元素是否存在 → 1/0SMISMEMBER tags "Java" "Python" # 批量判断(Redis 6.2+)→ [1, 0]
# 随机操作SRANDMEMBER tags # 随机返回 1 个元素(不删除)SRANDMEMBER tags 3 # 随机返回 3 个不重复元素SPOP tags # 随机弹出 1 个元素(删除)SPOP tags 2 # 随机弹出 2 个元素2.2. 集合运算
Section titled “2.2. 集合运算”SADD set1 a b c dSADD set2 c d e f
SINTER set1 set2 # 交集 → {c, d}SUNION set1 set2 # 并集 → {a, b, c, d, e, f}SDIFF set1 set2 # 差集(set1 中有但 set2 中没有)→ {a, b}
# 将运算结果存入新 key(不直接返回)SINTERSTORE result set1 set2 # 交集存入 resultSUNIONSTORE result set1 set2 # 并集存入 resultSDIFFSTORE result set1 set2 # 差集存入 result2.3. 命令速查
Section titled “2.3. 命令速查”| 命令 | 说明 | 复杂度 |
|---|---|---|
SADD key v1 v2 | 添加元素 | O(N) |
SREM key v1 | 删除元素 | O(N) |
SMEMBERS key | 获取全部元素 | O(N) |
SCARD key | 元素数量 | O(1) |
SISMEMBER key v | 判断是否存在 | O(1) |
SRANDMEMBER key n | 随机获取 n 个 | O(N) |
SPOP key n | 随机弹出 n 个 | O(N) |
SINTER / SUNION / SDIFF | 集合运算 | O(N*M) |
3. 底层编码
Section titled “3. 底层编码”Set 底层编码├── listpack(紧凑列表)│ → 元素数 ≤ 128 且每个元素为整数或长度 ≤ 64 字节│ → Redis 7.2+ 引入,替代旧版 intset/ziplist│├── intset(整数集合)│ → 所有元素均为整数且数量 ≤ 512│ → 内存极为紧凑,查找使用二分搜索│└── hashtable(哈希表) → 超过阈值后自动升级 → 查找 O(1)4. SpringBoot 实战
Section titled “4. SpringBoot 实战”4.1. 基础操作
Section titled “4.1. 基础操作”@Autowiredprivate StringRedisTemplate redisTemplate;
SetOperations<String, String> setOps = redisTemplate.opsForSet();
// 添加setOps.add("user:1001:tags", "Java", "Redis", "Spring");
// 查询Set<String> tags = setOps.members("user:1001:tags");Long size = setOps.size("user:1001:tags");Boolean exists = setOps.isMember("user:1001:tags", "Redis");
// 删除setOps.remove("user:1001:tags", "Spring");
// 随机取元素String one = setOps.randomMember("user:1001:tags");List<String> three = setOps.randomMembers("user:1001:tags", 3);
// 集合运算Set<String> inter = setOps.intersect("user:1001:follow", "user:1002:follow");Set<String> union = setOps.union("user:1001:follow", "user:1002:follow");Set<String> diff = setOps.difference("user:1001:follow", "user:1002:follow");4.2. 共同关注
Section titled “4.2. 共同关注”@Service@RequiredArgsConstructorpublic class FollowService {
private final StringRedisTemplate redisTemplate;
private String followKey(Long userId) { return "user:follow:" + userId; }
// 关注 public void follow(Long userId, Long targetId) { redisTemplate.opsForSet().add(followKey(userId), String.valueOf(targetId)); }
// 取关 public void unfollow(Long userId, Long targetId) { redisTemplate.opsForSet().remove(followKey(userId), String.valueOf(targetId)); }
// 是否已关注 public boolean isFollowing(Long userId, Long targetId) { return Boolean.TRUE.equals( redisTemplate.opsForSet().isMember(followKey(userId), String.valueOf(targetId)) ); }
// 共同关注(两人都关注的人) public Set<String> commonFollow(Long userId1, Long userId2) { return redisTemplate.opsForSet().intersect(followKey(userId1), followKey(userId2)); }
// 可能认识的人(对方关注但我没关注) public Set<String> recommendFollow(Long userId, Long targetId) { return redisTemplate.opsForSet().difference(followKey(targetId), followKey(userId)); }}4.3. 抽奖系统
Section titled “4.3. 抽奖系统”@Service@RequiredArgsConstructorpublic class LotteryService {
private final StringRedisTemplate redisTemplate;
// 参与抽奖 public void join(String activityId, Long userId) { redisTemplate.opsForSet().add("lottery:" + activityId, String.valueOf(userId)); }
// 不重复抽奖(中奖者不再参与) public List<String> draw(String activityId, int count) { // SPOP:弹出并从集合中删除,保证同一用户不会重复中奖 List<String> winners = new ArrayList<>(); for (int i = 0; i < count; i++) { String winner = redisTemplate.opsForSet().pop("lottery:" + activityId); if (winner != null) winners.add(winner); } return winners; }
// 可重复抽奖(大奖可重复参与) public List<String> drawRepeat(String activityId, int count) { // SRANDMEMBER:随机取元素但不删除 return redisTemplate.opsForSet().randomMembers("lottery:" + activityId, count); }}5. 典型业务场景
Section titled “5. 典型业务场景”| 场景 | 实现方式 | 关键命令 |
|---|---|---|
| 用户标签 | 每个用户一个 Set 存标签 | SADD / SMEMBERS |
| 共同好友 | 两个用户关注列表取交集 | SINTER |
| 好友推荐 | 对方关注列表与我的关注列表取差集 | SDIFF |
| UV 统计去重 | 用户 ID 加入 Set,SCARD 统计 | SADD / SCARD |
| 抽奖 | 参与者加入 Set,SPOP 随机弹出 | SPOP |
| 黑名单/白名单 | SADD 维护名单,SISMEMBER 验证 | SISMEMBER |