hweiyu00头像
Redis命令:SUNION封面图

Redis命令:SUNION

Redis SUNION 命令详细教程

SUNION 返回一个或多个集合的并集成员,结果天然去重。它从 Redis 1.0.0 起提供,复杂度为 O(N)(N 为所有参与运算集合的元素总数),并且带 @slow 标记——这意味着它不适合在超大集合上高频调用。不存在的 Key 按空集合处理。命令只读,不会修改任何数据。

资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338

一、概览与语法

SUNION key [key ...]
项目说明
数据类型Set
支持版本Redis 1.0.0 起
key一个或多个集合 Key(至少一个)
返回值数组,并集中的全部成员(去重)
时间复杂度O(N),N 为所有给定集合的元素总数
ACL@read、@set、@slow
命令标记readonly

官方说明:返回所有给定集合的并集成员,不存在的 key 被视为空集合。返回顺序不确定,调用方不应依赖顺序。$TRAE_REF

二、基础示例

以下命令在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。

DEL tutorial:{sunion}:key1 tutorial:{sunion}:key2 tutorial:{sunion}:key3
SADD tutorial:{sunion}:key1 "a" "b" "c" "d"
SADD tutorial:{sunion}:key2 "c"
SADD tutorial:{sunion}:key3 "a" "c" "e"
SUNION tutorial:{sunion}:key1 tutorial:{sunion}:key2 tutorial:{sunion}:key3
SCARD tutorial:{sunion}:key1

预期结果:三条 SADD 分别返回 4、1、3;SUNION 返回 a、b、c、d、e 共 5 个成员(顺序可能不同);SCARD 返回 4,说明源集合没有被修改。

命令只读,重复执行结果一致(成员集合相同,顺序可能变化)。

三、去重与顺序

特性说明
自动去重同一成员出现在多个集合中只返回一次
结果顺序不保证,属于 nondeterministic output order
是否保留重复不会,集合本身不允许重复成员
单 Key 调用等价于 SMEMBERS,仅返回顺序不同
空集合参与对于结果没有任何影响
DEL tutorial:{sunion}:order
SADD tutorial:{sunion}:order "1" "2" "3" "10"
SUNION tutorial:{sunion}:order

预期结果:返回 4 个成员,但顺序既不保证插入顺序、也不保证字典序或数值序(可能为 2、10、1、3)。需要对结果稳定排序时,在客户端排序(注意大结果集排序的内存开销),或改用 SORT key ALPHA。

因为顺序不确定,不要用 SUNION 的返回值做分页或增量计算的依据。分页应使用 SSCAN(配合 SSCAN 遍历每一个源集合),需要固定顺序的列表则应改用 Sorted Set。

四、不存在的 Key 与全空结果

DEL tutorial:{sunion}:nokey
SADD tutorial:{sunion}:only "x"
SUNION tutorial:{sunion}:nokey
SUNION tutorial:{sunion}:nokey tutorial:{sunion}:only
SUNION tutorial:{sunion}:nokey tutorial:{sunion}:nokey2
EXISTS tutorial:{sunion}:nokey

预期结果:第一条 SUNION 返回空数组 (empty array);第二条返回 x;第三条返回空数组;EXISTS 返回 0,说明只读命令不会创建不存在的 Key。

不存在的 Key 被视为空集合而不是错误,这让"多维度标签取并集"的实现变得简单:某个维度没有数据时不需要提前判断 Key 是否存在。

五、边界情况与错误处理

场景行为
未传任何 Key报参数数量错误(至少需要 1 个 Key)
任一 Key 不存在视为空集合,正常返回
任一 Key 类型不是 Set报 WRONGTYPE,整个命令失败
结果为单个成员仍返回数组,不会退化为字符串
结果为空返回空数组,不是 nil
集合编码为 intset结果与 hashtable 编码一致,成员相同
SET tutorial:{sunion}:str text
SUNION tutorial:{sunion}:str tutorial:{sunion}:key2
SUNION

预期结果:第一条因 str 是 String 类型报 WRONGTYPE(不会返回部分结果);第二条缺少 Key,报 ERR wrong number of arguments for 'sunion' command。

类型校验是整体失败:只要有一个 Key 类型不对,整个命令就报错,不会出现"前面几个集合的并集已经算出来了"的结果。这对业务的含义是:任何一个参与运算的 Key 被其他类型占用,依赖它的聚合逻辑会立刻失效,命名空间隔离(例如集合统一加 set: 前缀)比事后排查更省事。

六、与相近命令的区别

命令运算结果去向复杂度是否写数据
SUNION并集返回客户端O(N),N 为元素总数否
SUNIONSTORE并集写入 destinationO(N)是
SINTER交集返回客户端O(N×M) 最坏否
SDIFF差集返回客户端O(N)否
SMEMBERS单个集合全部成员返回客户端O(N),N 为集合大小否
SMISMEMBER若干成员是否存在返回 0/1 数组O(N),N 为参数个数否
DEL tutorial:{sunion}:cmp1 tutorial:{sunion}:cmp2
SADD tutorial:{sunion}:cmp1 "1" "2"
SADD tutorial:{sunion}:cmp2 "2" "3"
SUNION tutorial:{sunion}:cmp1 tutorial:{sunion}:cmp2
SINTER tutorial:{sunion}:cmp1 tutorial:{sunion}:cmp2
SDIFF tutorial:{sunion}:cmp1 tutorial:{sunion}:cmp2

预期结果:SUNION 返回 1、2、3;SINTER 返回 2;SDIFF 返回 1(差集有方向:cmp1 - cmp2)。

选择要点:

  • 要全部标签的大合集:SUNION。
  • 要结果缓存起来复用:SUNIONSTORE(避免每次重复计算与重复传输)。
  • 只要"有没有交集":用 SINTERCARD 判断数量是否大于 0,比取回全部交集轻量。
  • 只要成员是否属于某个集合:SMISMEMBER,不要做并集再比对。

七、客户端示例

前提为已安装 redis-py 并准备好本地测试实例。redis-py 的 sunion(keys) 返回成员集合(不同版本可能为 list,取值时建议先 set() 或 list() 转换)。

import redis

r = redis.Redis(host="localhost", port=6379, decode_responses=True)
k1 = "tutorial:{sunion}:py1"
k2 = "tutorial:{sunion}:py2"
try:
    r.delete(k1, k2)
    r.sadd(k1, "a", "b", "c")
    r.sadd(k2, "c", "d")

    print(sorted(r.sunion([k1, k2])))   # ['a', 'b', 'c', 'd']
    print(sorted(r.sunion([k1, "no-such"])))  # ['a', 'b', 'c'],不存在的 Key 视为空集合
finally:
    r.delete(k1, k2)
    r.close()

Java(Jedis)示例,sunion(keys...) 返回 Set<String>:

try (Jedis jedis = new Jedis("localhost", 6379)) {
    String k1 = "tutorial:{sunion}:java1";
    String k2 = "tutorial:{sunion}:java2";
    jedis.del(k1, k2);
    jedis.sadd(k1, "a", "b", "c");
    jedis.sadd(k2, "c", "d");

    System.out.println(jedis.sunion(k1, k2));   // [a, b, c, d],顺序不保证
    jedis.del(k1, k2);
}

注意返回类型差异:redis-cli 返回数组,Python 返回集合类对象,Jedis 返回 Set<String>。返回值本身无序,展示前先排序可以避免"结果每次不一样"的错觉。

八、原子性与并发

单命令原子:SUNION 在一次调用内完成全部集合的合并,返回的是某一时刻的一致性结果,不会出现"读了一半集合、另一个集合刚被改掉"的错位。

只读不产生副作用:不创建 Key、不修改成员、不影响 TTL。

不保证快照长期有效:命令返回后源集合仍可能被修改。如果业务需要"基于某个快照反复计算",应先用 SUNIONSTORE 落地结果,再对结果集合做后续处理。

事务与 Lua:可在 MULTI/EXEC、Lua、Pipeline 中使用。在 Lua 中 SUNION 的结果顺序同样不保证,脚本里不要依赖顺序做逻辑(例如取"第一个元素")。

副本读取:作为只读命令可在副本执行,但副本数据存在复制延迟,聚合结果可能滞后于主节点。

九、性能与使用注意事项

O(N) 且带 @slow 标记:N 是所有集合的元素总数。两个 100 万元的集合求并集,需要遍历 200 万元素并在内存中构建结果集,会长时间占用主线程。

结果需要传输到客户端:除了计算成本,还有网络与客户端内存成本。集合有 100 万成员时,响应可能有几十 MB。

重复计算是最大浪费:同一批集合的并集被反复请求时,用 SUNIONSTORE 落地并设置 TTL,读取方只做 SMEMBERS/SCARD,能把 O(N) 的计算变成一次计算 + 多次读取。

客户端分页的正确方式是不要用它:SUNION 无法分页;需要遍历大并集时,逐集合 SSCAN 并自行合并去重,或 SUNIONSTORE 后对结果集合 SSCAN。

集群限制:Redis Cluster 中所有 Key 必须在同一哈希槽,否则报 CROSSSLOT。给 Key 加相同哈希标签(例如 {tag}:set1、{tag}:set2)可以保证同槽,但要注意这会让这些集合集中到同一个节点。

内存峰值:命令执行期间需要在服务端构建临时结果集合,大集合上可能触发明显的内存抖动,必要时错峰执行。

十、典型业务场景

多维度标签合并:把用户在不同渠道获得的标签集合求并集,得到完整标签画像。

权限并集计算:用户属于多个角色,每个角色对应一个权限集合,登录时求并集写入会话缓存(通常配合 SUNIONSTORE)。

好友/关注去重:把多个来源(站内、导入、活动)的关注列表合并去重,生成统一名单。

黑名单合并:多个业务线的封禁名单求并集后做全局校验。

可售商品汇总:多个仓库的库存 ID 集合求并集,得到可售商品全集。

报告期数据合并:按月存储的活跃用户集合求并集,得到季度活跃用户。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/vbhfdghff/article/details/169557113

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--