Redis命令:SINTERCARD
Redis SINTERCARD 命令详细教程
SINTERCARD返回多个集合交集的成员数量,而不返回成员本身。它是 Redis 7.0.0 新增的命令,专为"只关心交集大小"的场景设计:省去把全部交集成员传输到客户端的开销,并支持LIMIT参数在达到阈值后立即停止计算。判断"两人是否有共同好友""商品是否满足全部筛选条件"时,用它比 SINTER 更省资源。
资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338
一、概览与语法
SINTERCARD numkeys key [key ...] [LIMIT limit]| 项目 | 说明 |
|---|---|
| 数据类型 | Set |
| 支持版本 | Redis 7.0.0 起 |
| numkeys | 必填,参与运算的集合个数,必须与实际提供的 Key 数量一致 |
| key | 参与求交的集合 Key,数量由 numkeys 指定 |
| LIMIT limit | 可选,默认 0 表示不限制;大于 0 时达到该数量即停止计算并返回 limit |
| 返回值 | 整数,交集的成员数量 |
| 时间复杂度 | O(N×M) 最坏情况,N 为最小集合成员数,M 为集合个数;带 LIMIT 时可提前终止 |
| ACL | @read、@set、@slow |
| 命令标记 | readonly |
官方说明:返回指定集合交集的基数。不存在的 Key 被视为空集合;可选的 LIMIT 参数指定计算的上限,达到上限即返回,默认值 0 表示不限制。$TRAE_REF
numkeys是 Redis 7.0 一批"可变参数"命令(如 ZINTERCARD、LMPOP、ZMPOP)的统一设计:先用一个整数声明后面跟几个 Key,再由服务端校验数量。
二、基础示例
以下命令在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。示例沿用官方文档的数据与结果。
DEL tutorial:{sintercard}:key1 tutorial:{sintercard}:key2 SADD tutorial:{sintercard}:key1 "a" "b" "c" "d" SADD tutorial:{sintercard}:key2 "a" "c" "d" "e" SINTERCARD 2 tutorial:{sintercard}:key1 tutorial:{sintercard}:key2 SINTERCARD 2 tutorial:{sintercard}:key1 tutorial:{sintercard}:key2 LIMIT 2预期结果:两条 SADD 各返回4;第一条 SINTERCARD 返回(integer) 3(交集为 a、c、d);第二条带LIMIT 2返回(integer) 2,因为数到第 2 个交集成员就提前终止。
三、LIMIT 的语义
这是 SINTERCARD 相对 SINTER 最核心的价值,也是最容易误解的地方:
LIMIT 0(默认):不限制,返回完整交集基数。LIMIT n(n > 0):最多数到 n 就停,返回值为min(真实交集大小, n)。LIMIT为负数:报错,不会当作"不限制"。
因此LIMIT n返回n只能说明"交集至少有 n 个",不能说明恰好有 n 个。业务上常用于"是否存在至少 n 个共同点"的布尔判断:返回 n 即条件成立,返回小于 n 即条件不成立。
SINTERCARD 2 tutorial:{sintercard}:key1 tutorial:{sintercard}:key2 LIMIT 1 SINTERCARD 2 tutorial:{sintercard}:key1 tutorial:{sintercard}:key2 LIMIT 10预期结果:LIMIT 1返回1(交集至少 1 个,实际是 3 个);LIMIT 10返回3(阈值大于真实交集,返回完整数量)。
四、numkeys 的校验
numkeys必须与实际 Key 数量严格一致,否则报错:
| 情形 | 结果 |
|---|---|
| numkeys 与实际 Key 数一致 | 正常执行 |
| numkeys 小于实际 Key 数 | 多余的参数会被当作语法错误处理 |
| numkeys 大于实际 Key 数 | 报错,提示 Key 数量不足 |
| numkeys 为 0 或负数 | 报错,必须是正整数 |
| numkeys 不是整数 | 报ERR value is not an integer or out of range |
SINTERCARD 2 tutorial:{sintercard}:key1 SINTERCARD 0 tutorial:{sintercard}:key1预期结果:第一条声明 2 个 Key 却只给了 1 个,报参数数量错误;第二条 numkeys 为 0,报 numkeys 必须大于 0 的错误。
五、不存在的 Key 与空结果
| 场景 | 返回 |
|---|---|
| 所有 Key 都存在且有交集 | 交集成员数量(正整数) |
| 任一 Key 不存在 | 0(不存在的集合视为空集) |
| 所有 Key 都存在但无公共成员 | 0 |
SINTERCARD 2 tutorial:{sintercard}:key1 tutorial:{sintercard}:nokey预期结果:返回0。与 SINTER 一致,任一 Key 不存在即交集为空——这与差集 SDIFF 的"后续 Key 不存在不影响结果"形成鲜明对比。
六、边界情况与错误处理
| 场景 | 行为 |
|---|---|
| 缺少 numkeys | 报语法错误 |
| numkeys 与实际 Key 数不符 | 报参数数量错误 |
| LIMIT 为负数 | 报LIMIT can't be negative |
| LIMIT 不是整数 | 报ERR value is not an integer or out of range |
| 任一 Key 类型不是 Set | 报 WRONGTYPE |
| 只传一个 Key(numkeys 1) | 返回该集合的基数 |
SET tutorial:{sintercard}:str text SINTERCARD 2 tutorial:{sintercard}:key1 tutorial:{sintercard}:str SINTERCARD 2 tutorial:{sintercard}:key1 tutorial:{sintercard}:key2 LIMIT -1预期结果:第一条因 str 是 String 类型报 WRONGTYPE;第二条因 LIMIT 为负报LIMIT can't be negative。
七、与相近命令的区别
| 命令 | 返回 | 是否传输成员 | LIMIT 支持 | 版本 |
|---|---|---|---|---|
| SINTERCARD | 交集数量 | 否 | 是 | 7.0.0 |
| SINTER | 交集成员数组 | 是 | 否 | 1.0.0 |
| SINTERSTORE | 写入目标后返回数量 | 否(写服务端) | 否 | 1.0.0 |
| SUNION | 并集成员 | 是 | 否 | 1.0.0 |
| SDIFF | 差集成员 | 是 | 否 | 1.0.0 |
| ZINTERCARD | 有序集合交集数量 | 否 | 是 | 7.0.0 |
选择规则很清楚:只关心"有多少"或"是否达到阈值"用 SINTERCARD;需要成员内容用 SINTER;需要成员在服务端长期保留用 SINTERSTORE。
SINTERCARD 与 ZINTERCARD 是同一设计的集合版与有序集合版,参数结构一致(numkeys + keys + LIMIT),但 ZINTERCARD 不支持 WEIGHTS/AGGREGATE。
八、客户端示例
前提为 redis-py 版本支持 SINTERCARD(7.0 及以上服务端)。redis-py 提供sintercard(keys, limit=0)。
importredis r=redis.Redis(host="localhost",port=6379,decode_responses=True)k1="tutorial:{sintercard}:py1"k2="tutorial:{sintercard}:py2"try:r.delete(k1,k2)r.sadd(k1,"a","b","c","d")r.sadd(k2,"a","c","d","e")print(r.sintercard([k1,k2]))# 3print(r.sintercard([k1,k2],2))# 2,达到 LIMIT 提前终止print(r.sintercard([k1],0))# 4,单集合即自身基数print(r.sintercard([k1,"no-such"]))# 0finally:r.delete(k1,k2)r.close()Java(Jedis 5.x)示例,sintercard(keys...)与sintercard(limit, keys...)返回long。Jedis 会自动计算 numkeys,不需要手动传入:
try(Jedisjedis=newJedis("localhost",6379)){Stringk1="tutorial:{sintercard}:java1";Stringk2="tutorial:{sintercard}:java2";jedis.del(k1,k2);jedis.sadd(k1,"a","b","c","d");jedis.sadd(k2,"a","c","d","e");System.out.println(jedis.sintercard(k1,k2));// 3System.out.println(jedis.sintercard(2,k1,k2));// 2,limit=2 提前终止jedis.del(k1,k2);}注意sintercard(2, k1, k2)中的 2 是limit 而不是 numkeys,这是 Jedis 与原始命令参数顺序上的差异,容易写错。Jedis 较早版本(4.x 及之前)没有该方法,需升级到 5.x 或自行用sendCommand发送原始命令。
九、原子性与并发
只读且原子:SINTERCARD 不修改数据,单命令原子,返回执行瞬间的计数快照。
计数是精确值:结果没有误差,与 SINTER 返回数组的长度一致(未设 LIMIT 时)。这与 PFCOUNT 的近似基数不同。
LIMIT 与并发:带 LIMIT 时返回n只代表"至少 n 个",集合在并发写入下随时可能变化,不要把该结果当作稳定不变的精确值缓存。
事务与 Pipeline:可在 MULTI/EXEC、Lua 脚本和管道中正常使用。
集群限制:Redis Cluster 中所有参与运算的 Key 必须落在同一哈希槽,否则报 CROSSSLOT。
十、性能与使用注意事项
省的是带宽而非计算:SINTERCARD 与 SINTER 的复杂度同为 O(N×M),差别在于不把成员序列化并传回客户端。交集很大时,节省的网络与内存开销非常显著。
LIMIT 才是真正的提速手段:达到阈值即停止遍历,能把"判断是否存在共同点"从全量扫描降为常数级。判断"是否有共同好友"时,LIMIT 1是标准写法。
仍然可能阻塞:不带 LIMIT 的大集合求交依旧带@slow标记。必要时在副本执行,或先做数据分片。
不能替代"读成员":SINTERCARD 只返回数量。后续需要用成员时,仍需 SINTER 或 SINTERSTORE。
十一、典型业务场景
共同好友数量:SINTERCARD 2 user:{a}:friends user:{b}:friends直接得到共同好友数,不必取回全部集合再求长度。若只需判断"有没有共同好友",加LIMIT 1。
多条件筛选取量:商品颜色、尺寸、品牌三个集合求交的基数,用于展示"当前筛选条件下共 N 件商品",避免传输大量 ID。
权限覆盖率判断:用户角色集合与所需角色集合的交集数量是否达到要求,用 LIMIT 做快速判定。
推荐去重计数:候选集合与已曝光集合的交集数量,用于评估推荐重复率。
风控同时命中规则数:多个规则集合的交集大小,作为风险评分输入。