广告位联系
返回顶部

Redis命令SDIFF详细介绍

Redis 来源:互联网 作者:佚名 发布时间:2026-10-22 21:55:57 人浏览
摘要

SDIFF计算多个集合的差集:返回第一个集合中存在、且不在任何后续集合中出现的成员。它从 Redis 1.0.0 起提供,是集合三大运算(差集 SDIFF、交集 SINTER、并集 SUNION)之一。差集运算是有方向的

SDIFF 计算多个集合的差集:返回第一个集合中存在、且不在任何后续集合中出现的成员。它从 Redis 1.0.0 起提供,是集合三大运算(差集 SDIFF、交集 SINTER、并集 SUNION)之一。差集运算是有方向的,SDIFF A B 与 SDIFF B A 结果通常不同,这是使用时最容易出错的地方。

一、概览与语法

1

SDIFF key [key ...]

项目 说明
数据类型 Set
支持版本 Redis 1.0.0 起
key 至少一个集合 Key;第一个是被减数,其余是要排除的集合
返回值 数组,差集结果中的成员;无结果时返回空数组
时间复杂度 O(N),N 为所有参与集合的成员总数
ACL @read、@set、@slow
命令标记 readonly、sort_for_script

官方说明:返回第一个集合与所有后续集合之间的差集,即第一个集合中不存在于任何后续集合的成员。不存在的 Key 被视为空集合。

差集的数学表达:SDIFF A B C = A − (B ∪ C)。注意是减去后续集合的并集,不是逐个依次相减。

二、基础示例

以下命令在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。示例沿用官方文档的数据与结果。

1

2

3

4

5

DEL tutorial:{sdiff}:key1 tutorial:{sdiff}:key2 tutorial:{sdiff}:key3

SADD tutorial:{sdiff}:key1 "a" "b" "c" "d"

SADD tutorial:{sdiff}:key2 "c"

SADD tutorial:{sdiff}:key3 "a" "c" "e"

SDIFF tutorial:{sdiff}:key1 tutorial:{sdiff}:key2 tutorial:{sdiff}:key3

预期结果:三条 SADD 分别返回 4、1、3;SDIFF 返回 "b"、"d"。key1 中的 a 被 key3 排除,c 被 key2 与 key3 排除,只剩 b、d。

三、差集的方向性

差集不满足交换律,参数的书写顺序直接决定结果:

1

2

SDIFF tutorial:{sdiff}:key1 tutorial:{sdiff}:key2

SDIFF tutorial:{sdiff}:key2 tutorial:{sdiff}:key1

预期结果:第一条返回 "a"、"b"、"d"(key1 去掉 c);第二条返回空数组(key2 只有 c,而 c 在 key1 中)。

这组对比说明:第一个 Key 是基准集合,后面的 Key 只用于排除。业务上想表达"我有而你没有",必须是"我"在前。

四、多集合语义与不存在的 Key

场景 结果
第一个 Key 不存在 空数组(空集减任何集合仍为空)
某个后续 Key 不存在 视为空集合,不影响结果
所有 Key 都存在但无差异 空数组
参与运算的 Key 中有非集合类型 报 WRONGTYPE

1

2

3

4

DEL tutorial:{sdiff}:empty tutorial:{sdiff}:exist

SADD tutorial:{sdiff}:exist "x"

SDIFF tutorial:{sdiff}:empty tutorial:{sdiff}:exist

SDIFF tutorial:{sdiff}:exist tutorial:{sdiff}:empty

预期结果:第一条 SDIFF 返回空数组(基准为空集);第二条返回 "x"(后续 Key 不存在视为空集合,不影响结果)。

不存在的 Key 不报错这一点很实用:用差集做"未读列表"时,即使用户从未有过已读记录,也不必先创建空集合。

五、返回值与空结果的区分

SDIFF 只返回数组,不返回计数:

情形 返回
存在差集成员 成员数组,顺序不保证
差集为空 空数组,不返回空值
Key 类型错误 报 WRONGTYPE

空数组是正常结果,表示"没有差异",不是错误。返回值没有顺序保证:集合本身无序,SDIFF 的输出顺序也不代表任何业务含义。需要稳定顺序时,应在客户端自行排序,或把结果用 SDIFFSTORE 落地后借助 SORT 处理。

需要结果数量时,客户端取数组长度即可;若只关心数量而不需要成员,Redis 7.0 起可用 SINTERCARD(仅交集),差集没有对应的 count 版本,仍需取回结果或改用 SDIFFSTORE。

六、边界情况与错误处理

场景 行为
只传一个 Key 返回该集合的全部成员(相当于复制)
未传任何 Key 报语法错误
任一 Key 类型不是 Set 报 WRONGTYPE
成员为空字符串 正常参与运算
结果成员数超过 2^32 - 1 不可能,受集合上限约束

1

2

3

4

SDIFF tutorial:{sdiff}:key1

SET tutorial:{sdiff}:str text

SDIFF tutorial:{sdiff}:key1 tutorial:{sdiff}:str

SDIFF

预期结果:第一条返回 key1 的全部成员 a、b、c、d;第二条因包含 String 类型报 WRONGTYPE;第三条报 ERR wrong number of arguments for 'sdiff' command。

七、与相近命令的区别

命令 语义 结果写入 复杂度
SDIFF 差集 A − (B ∪ C) 只返回 O(N)
SDIFFSTORE 差集并存入 destination 写入目标 Key O(N)
SINTER 交集 A ∩ B ∩ C 只返回 O(N×M)
SUNION 并集 A ∪ B ∪ C 只返回 O(N)
SINTERCARD 交集基数(可带 LIMIT) 只返回数量 O(N×M)
SMEMBERS 单个集合全部成员 只返回 O(N)

SDIFFSTORE 是 SDIFF 的写版本,形式为 SDIFFSTORE destination key [key ...],返回写入结果的成员数量。它会把结果覆盖写入 destination:如果 destination 恰好是参与运算的某个 Key,结果会先计算再覆盖,语义上是安全的,但容易被误读,建议使用独立的 Key。

差集与交集、并集的组合可以表达复杂的集合逻辑,例如"关注了我但我不关注的人"(差集)、“互相关注”(交集)、“所有相关用户”(并集)。

八、客户端示例

前提为已安装 redis-py 并准备好本地测试实例。redis-py 的 sdiff(keys, *args) 返回 set,参数可传列表或逐个展开。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

import redis

r = redis.Redis(host="localhost", port=6379, decode_responses=True)

k1 = "tutorial:{sdiff}:py1"

k2 = "tutorial:{sdiff}:py2"

k3 = "tutorial:{sdiff}:py3"

try:

    r.delete(k1, k2, k3)

    r.sadd(k1, "a", "b", "c", "d")

    r.sadd(k2, "c")

    r.sadd(k3, "a", "c", "e")

    print(r.sdiff([k1, k2, k3]))   # {'b', 'd'},返回 set

    print(r.sdiff(k1, k2))         # {'a', 'b', 'd'}

    print(r.sdiff("no-such-1", "no-such-2"))  # set(),基准不存在

finally:

    r.delete(k1, k2, k3)

    r.close()

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

1

2

3

4

5

6

7

8

9

10

11

12

try (Jedis jedis = new Jedis("localhost", 6379)) {

    String k1 = "tutorial:{sdiff}:java1";

    String k2 = "tutorial:{sdiff}:java2";

    String k3 = "tutorial:{sdiff}:java3";

    jedis.del(k1, k2, k3);

    jedis.sadd(k1, "a", "b", "c", "d");

    jedis.sadd(k2, "c");

    jedis.sadd(k3, "a", "c", "e");

    System.out.println(jedis.sdiff(k1, k2, k3));  // [b, d],顺序不保证

    System.out.println(jedis.sdiff(k1, k2));      // [a, b, d]

    jedis.del(k1, k2, k3);

}

九、原子性与并发

只读且原子:SDIFF 不修改任何数据,单命令原子,返回执行瞬间的差集快照,不会读取到"部分更新"的中间状态。

结果可能立即过期:返回的是快照,命令结束后集合仍可被其他客户端修改。若业务需要"读到之后持续使用这份差异",应在客户端保存结果,或用 SDIFFSTORE 落库到独立 Key。

事务与 Pipeline:可在 MULTI/EXEC、Lua 脚本和管道中正常使用。在 Lua 脚本中结果会被排序(命令标记含 sort_for_script),便于脚本逻辑稳定判断。

集群限制:Redis Cluster 中所有参与运算的 Key 必须落在同一哈希槽,否则报 CROSSSLOT。给多个 Key 加相同哈希标签(如 tutorial:{sdiff}:key1、tutorial:{sdiff}:key2)即可保证同槽。

十、性能与使用注意事项

复杂度 O(N):N 是所有参与集合的成员总数。Redis 会先选一个集合作为基准逐成员检查,实际成本与最坏情况下的成员总数相关。参与运算的集合越大、Key 越多,耗时越长,且带 @slow 标记。

大集合慎用:百万级集合做差集会长时间占用主线程,阻塞其他命令。可考虑:

  • 在只读副本上执行;
  • 用 SSCAN 分批在客户端计算;
  • 用 SDIFFSTORE 异步落地后再消费(仍会阻塞);
  • 从数据模型上避免超大集合,按时间或用户分片。

结果传输开销:SDIFF 会把全部结果成员传回客户端,结果很大时网络开销显著。只关心数量时,可用 SDIFFSTORE 到一个临时 Key 再 SCARD,或用 Lua 脚本在服务端求长度(注意脚本同样阻塞)。

内存:差集运算在服务端不分配持久内存,但计算过程可能用到临时集合结构,超大集合时需留意内存峰值。

十一、典型业务场景

未读消息列表:全部消息集合减去已读集合,得到未读消息 ID;即使用户从未读过,已读集合不存在也不影响结果。

推荐候选过滤:候选内容集合减去已曝光/已购买集合,得到本次可推荐的集合。

权限差异对比:用户当前权限集合减去应有权限集合,找出越权项;反向相减则找出缺失权限。

好友关系分析:关注集合减去互关集合,得到"单向关注"的人。

数据同步差异:两个版本的数据 Key 集合做差,找出新增或缺失项。

标签补全:目标标签集合减去用户已有标签集合,得到还需要打的标签。

十二、练习、排错与总结

练习:新建 tutorial:{sdiff}:exa、tutorial:{sdiff}:exb、tutorial:{sdiff}:exc;向 exa 写入 1 2 3 4 5,向 exb 写入 2 4,向 exc 写入 5 6;执行 SDIFF exa exb exc 期望得到 1、3;执行 SDIFF exb exa 期望空数组;执行 SDIFF exa not-exist 期望返回 exa 的全部成员;用 SDIFFSTORE 把结果写入 tutorial:{sdiff}:exout 并用 SCARD 验证数量为 2。

排错要点:结果与预期相反时先检查参数顺序,第一个 Key 才是基准;结果比预期多出成员时,检查后续 Key 是否拼写错误或写成了不存在的 Key(不存在的 Key 视为空集,不会排除任何成员);报 WRONGTYPE 时用 TYPE 逐个检查参与运算的 Key;报 CROSSSLOT 时给所有 Key 加相同哈希标签;大集合上命令阻塞时考虑副本执行或客户端分批;结果顺序与预期不符属正常,集合无序。清理使用 DEL tutorial:{sdiff}:key1 tutorial:{sdiff}:key2 tutorial:{sdiff}:key3 tutorial:{sdiff}:empty tutorial:{sdiff}:exist tutorial:{sdiff}:str tutorial:{sdiff}:py1 tutorial:{sdiff}:py2 tutorial:{sdiff}:py3 tutorial:{sdiff}:java1 tutorial:{sdiff}:java2 tutorial:{sdiff}:java3 tutorial:{sdiff}:exa tutorial:{sdiff}:exb tutorial:{sdiff}:exc tutorial:{sdiff}:exout。速记:1.0 起支持、第一个集合减去后续集合的并集、有方向不可交换、不存在的 Key 视为空集、无结果返回空数组、结果为快照无序、复杂度 O(N) 大集合慎用、SDIFFSTORE 可落库、集群中必须同槽、与 SINTER/SUNION 构成集合三运算。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计