Redis 集合(Set)

Redis 集合(Set)完全攻略

“无序 + 自动去重” —— 标签系统、抽奖池、共同好友、权限控制… 去重神器!


一、Set 核心特点

特性说明
底层结构intset(小整数集合) 或 hashtable(通用)
无序不保证插入顺序
自动去重同一元素只存一次
最大成员数2^32 – 1(约 42 亿)
核心操作 O(1)SADD / SREM / SISMEMBER
集合运算交集、并集、差集(支持多 key)
内部编码小整数 → intset,其他 → hashtable

二、Set 命令全表(共 18 个)

命令说明示例
SADD key m1 m2添加成员SADD tags "redis" "cache"
SREM key m1删除成员SREM tags "old"
SMEMBERS key获取全部成员SMEMBERS tags
SCARD key成员数量SCARD tags
SISMEMBER key m是否存在SISMEMBER tags "redis"
SRANDMEMBER key [count]随机返回(不删除)SRANDMEMBER users 3
SPOP key [count]随机弹出(删除)SPOP lottery 1
SMOVE src dest m原子移动SMOVE set:a set:b "x"
SINTER k1 k2交集SINTER friends:1 friends:2
SUNION k1 k2并集SUNION set:a set:b
SDIFF k1 k2差集SDIFF followers:1 followings:1
SINTERSTORE dest k1 k2交集存入新 keySINTERSTORE common:ab a b
SUNIONSTORE dest k1 k2并集存入新 keySUNIONSTORE all:ab a b
SDIFFSTORE dest k1 k2差集存入新 keySDIFFSTORE diff:ab a b
SSCAN key cursor [MATCH p]渐进式遍历SSCAN tags 0 MATCH r*

三、核心实战场景


1. 标签系统(用户/文章标签)

# 给用户添加标签
SADD user:1001:tags "golang" "redis" "docker"

# 查询用户标签
SMEMBERS user:1001:tags

# 删除标签
SREM user:1001:tags "docker"

2. 抽奖系统(公平随机)

# 奖池
SADD lottery:2025 "u1" "u2" "u3" "u4" "u5"

# 随机抽 3 人(不重复)
SRANDMEMBER lottery:2025 3

# 或者弹出中奖者(移除)
SPOP lottery:2025 1

3. 共同好友 / 推荐系统

# 用户 A 的好友
SADD friends:1001 "u2" "u3" "u4" "u5"

# 用户 B 的好友
SADD friends:1002 "u3" "u4" "u6"

# 共同好友
SINTER friends:1001 friends:1002
# → "u3" "u4"

4. 权限控制(角色 → 权限集合)

SADD role:admin "user:delete" "post:edit" "system:restart"
SADD role:user  "post:view" "comment:add"

# 检查权限
SISMEMBER role:admin "user:delete"   → 1

5. 去重统计(UV、IP 去重)

# 每天访问用户去重
SADD uv:20251112 "u1" "u2" "u1" "u3"
SCARD uv:20251112   → 3

# 保留7天
EXPIRE uv:20251112 604800

更省内存?用 HyperLogLog


四、Set vs List vs Hash 对比

场景推荐
需要去重Set
保持顺序List
字段-值映射Hash
频繁中间插入List
集合运算Set

五、内部编码优化(intset vs hashtable)

# 小整数集合 → intset(超省内存)
SADD nums 1 2 3 4 5
OBJECT ENCODING nums   → "intset"

# 含字符串 → hashtable
SADD mixed "a" "b" 1
OBJECT ENCODING mixed  → "hashtable"

切换阈值(配置文件)

配置默认值说明
set-max-intset-entries512成员 > 512 或非整数 → hashtable

生产建议:保持默认


六、性能与内存优化建议

建议说明
避免大 Set> 100万成员 → 拆分或用 HyperLogLog
SSCAN 替代 SMEMBERS大集合遍历
集合运算前估算大小避免 O(N) 卡顿
设置 TTL防止内存泄漏
Pipeline 批量操作减少 RTT
# 安全遍历大 Set
SSCAN myset 0 MATCH "user:*" COUNT 100

七、大 Set 排查与拆分

排查大 Set

redis-cli --bigkeys
# Biggest set found "online:users" with 5000000 members

拆分方案

# 原:SADD online:users "u1" "u2" ...
# 拆:按地区/时间分片
SADD online:users:beijing "u1"
SADD online:users:shanghai "u2"

八、一键速查表

# 基础
SADD s a b c
SREM s c
SMEMBERS s
SCARD s
SISMEMBER s a

# 随机
SRANDMEMBER s 2
SPOP s 1

# 集合运算
SINTER a b
SUNION a b
SDIFF a b

# 存储结果
SINTERSTORE common a b

# 遍历
SSCAN s 0 MATCH r*

九、客户端代码示例

Python (redis-py)

import redis
r = redis.Redis()

# 标签系统
r.sadd('user:1001:tags', 'python', 'redis', 'ai')
r.srem('user:1001:tags', 'ai')
tags = r.smembers('user:1001:tags')
print(tags)  # {b'python', b'redis'}

# 共同好友
common = r.sinter('friends:1001', 'friends:1002')
print(common)

# 抽奖
winner = r.spop('lottery:2025')

Go (go-redis)

ctx := context.Background()
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})

rdb.SAdd(ctx, "tags", "golang", "redis")
exists, _ := rdb.SIsMember(ctx, "tags", "redis").Result()

十、常见问题与解决方案

问题原因解决方案
内存暴涨Set 无限增长加 TTL 或分片
SMEMBERS 卡顿成员 > 100万改用 SSCAN
集合运算慢输入集合太大预估大小,拆分
去重不生效使用 List改用 Set

十一、Set 在高并发中的妙用

1. 在线用户去重

# 用户上线
SADD online:users "u:1001"
EXPIRE online:users 300   # 5分钟心跳

# 用户下线
SREM online:users "u:1001"

# 当前在线人数
SCARD online:users

2. 黑名单系统

SADD blacklist:ip "1.2.3.4" "5.6.7.8"
SISMEMBER blacklist:ip "1.2.3.4" → 1 → 拒绝访问

完成!你已精通 Redis Set!

# 一行命令体验 Set 全功能
redis-cli <<EOF
SADD s a b c a
SMEMBERS s
SCARD s
SRANDMEMBER s 2
SPOP s
SINTER s s2
SSCAN s 0
EOF

下一步推荐

  1. Redis Sorted Set 排行榜
  2. Set + Lua 实现复杂抽奖逻辑
  3. HyperLogLog vs Set 去重对比

需要我送你

  • “百万级抽奖系统(Set + Lua 防刷)”
  • “社交共同好友推荐引擎”
  • “动态标签系统(Set + Hash)”

回复:抽奖 | 共同好友 | 标签系统 即可!

文章已创建 2481

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部