用 Set 做标签是什么
标签系统的经典做法是「一个标签一个 Set」:把对象 ID 作为成员放进 tag:<标签名> 集合,于是「同时命中多个标签」就是集合交集 SINTER,「至少命中一个」是并集 SUNION,「只要第一个标签不要第二个」是差集 SDIFF。这种结构天然去重、支持 O(1) 的增删,缺点是集合规模大到百万级时,实时交集运算会变成慢命令。
基本信息
| 适用版本 | Redis 全版本(SINTERCARD 需 7.0+) |
|---|---|
| 打标 | SADD tag:<标签> <对象ID> |
| 交集 | SINTER / SINTERCARD 数量统计 |
| 并集与差集 | SUNION、SDIFF、SDIFFSTORE |
| 去重 | 集合天然去重,重复 SADD 不增加基数 |
| 规模上限 | 单标签集合过大时交集成本高,需分片或改用检索引擎 |
典型使用场景
- 内容推荐:找同时命中多个兴趣标签的文章或商品
- 共同关注:两个用户的好友集合求交
- 权限校验:角色集合与所需权限集合求交判断是否放行
- 人群圈选:多个条件标签求交得到目标人群
- 黑名单与白名单快速判断:SISMEMBER 单点判定 O(1)
实战
实战
SADD tag:redis post1 post2 post9
SADD tag:mysql post2 post3 post9
SINTER tag:redis tag:mysql # post2 post9(两个标签都命中)
SUNION tag:redis tag:mysql # 至少命中一个标签
SDIFF tag:redis tag:mysql # 只有 redis 标签的
SINTERCARD 2 tag:redis tag:mysql # 2:只取交集数量,省带宽
SISMEMBER tag:redis post1 # 1:单点判断是否命中标签
SCARD tag:redis # 单标签规模,判断是否过大集合运算选择
| 需求 | 命令 | 注意 |
|---|---|---|
| 同时满足多标签 | SINTER / SINTERCARD | 结果集大小取最小集合规模 |
| 至少满足一个 | SUNION / SUNIONSTORE | 结果可能远大于单个集合 |
| 排除某标签 | SDIFF | 方向敏感,A 减 B 与 B 减 A 不同 |
| 只要数量 | SINTERCARD | 7.0+ 支持,避免把大结果集传回客户端 |
| 单标签判定 | SISMEMBER | O(1),适合鉴权与前置校验 |
标签集合要按关注度预估规模:热点标签可能达到千万级成员,此时实时交集运算会让单线程阻塞。可用的缓解手段是把运算放到只读从库、离线预计算交集结果,或用搜索引擎承担标签检索。
不要把「多个标签」塞进一个 Set 当作多值字段:这样无法表达「同时满足多个标签」的语义,也无法按标签单独统计。正确结构是一标签一集合,对象 ID 作为成员。