在涉及标签匹配、共同好友、商品筛选等业务场景时,经常需要计算多个集合的交集。Redis提供了SINTER命令来获取交集结果,但每次调用都要把结果传回客户端再处理。如果希望把交集结果直接持久化到Redis中的一个新集合里,SINTERSTORE就是为此而生的命令。它在一个原子操作内完成计算与存储,省去了网络往返和二次写入的开销,是处理集合交集落盘需求的推荐方案。

SINTERSTORE的基本语法与执行流程
SINTERSTORE的语法非常简洁:SINTERSTORE destination key [key ...]。第一个参数destination是目标集合的键名,后面的参数是参与交集计算的源集合键。命令执行时,Redis会在服务端先把所有源集合的交集计算出来,然后用结果整体替换destination键的内容,最后返回结果集的元素数量。
需要特别注意的是,destination键如果已经存在,无论它之前是字符串、列表还是集合类型,都会被直接覆盖,旧值不做任何保留。这一点在复用目标键名时要格外小心,避免误删线上数据。另外有一个容易被忽视的细节:当参与计算的集合中存在不存在的键,或者交集结果为空时,SINTERSTORE不会创建一个空集合,而是会把destination键直接删除。这与Redis对空集合不占存储的设计哲学是一致的,集合元素被删空后键本身就消失了。
import redis
r = redis.Redis(host='127.0.0.1', port=6379, decode_responses=True)
# 构造两个标签集合
r.sadd('tag:python', 'user:1', 'user:2', 'user:3')
r.sadd('tag:redis', 'user:2', 'user:3', 'user:4')
# 计算交集并存储到新集合 result:key
count = r.sinterstore('result:key', 'tag:python', 'tag:redis')
print(count) # 输出 2
print(r.smembers('result:key')) # 输出 {'user:2', 'user:3'}
# 空交集时目标键会被删除
r.sinterstore('empty:result', 'tag:python', 'no:exists:key')
print(r.exists('empty:result')) # 输出 0,键不存在
从执行流程上看,Redis内部会对参与计算的集合按元素数量从小到大排序,先遍历最小的集合,再用它的每个元素去其他集合里做存在性检查,因此即使某个集合非常大,只要交集计算以小集合为起点,性能依然可控。
SINTERSTORE与SINTER的区别及选型建议
SINTER只返回交集结果,不产生任何键的变更,适合需要把结果拿到应用层进一步加工的场景,比如在内存中做分页、过滤或格式化后返回给前端。而SINTERSTORE把结果留在Redis服务端,适合结果需要被多个客户端共享、需要长期保留或需要基于结果做二次集合运算的场景。
两者在数据传输上的差异值得权衡。假设两个集合的交集有一百万个元素,SINTER会把这一百万个元素全部通过网络传回客户端,占用大量带宽和客户端内存;SINTERSTORE则只返回一个整数,数据完全留在服务端,网络开销几乎可以忽略。当然代价是Redis会为结果集合分配内存,如果结果集非常大且使用频繁,要评估实例的内存水位。
| 对比维度 | SINTER | SINTERSTORE |
|---|---|---|
| 返回内容 | 交集中的所有元素 | 结果集元素个数 |
| 是否生成新键 | 否 | 是,写入destination键 |
| 网络传输量 | 与交集大小成正比 | 固定,仅一个整数 |
| 空交集行为 | 返回空数组 | 删除destination键 |
| 适用场景 | 即时查询、结果加工 | 结果复用、服务端留存 |
还有一个实践中的常见坑:如果把destination设置成了参与计算的源键之一,比如执行SINTERSTORE myset myset other,Redis是允许的,它会先在内存中完成交集计算,再统一替换目标键,计算结果不会因为键被覆盖而受影响。但为了代码可读性和排查问题的便利,一般不建议这样写,明确使用独立的结果键名更清晰。
典型应用场景与组合用法示例
以电商商品筛选为例,用户同时勾选了多个筛选标签,每个标签对应一个商品ID集合。这时用SINTERSTORE把满足所有标签的商品写入一个结果集合,再配合SCARD获取总数、SRANDMEMBER随机抽取展示,整个流程都在Redis内完成,响应速度非常快。结果集还可以设置过期时间,避免临时数据长期占用内存。
127.0.0.1:6379> SADD filter:brand:huawei 1001 1002 1003 1004 (integer) 4 127.0.0.1:6379> SADD filter:price:2000-4000 1002 1003 1005 (integer) 3 127.0.0.1:6379> SINTERSTORE filter:result filter:brand:huawei filter:price:2000-4000 (integer) 2 127.0.0.1:6379> SMEMBERS filter:result 1) "1002" 2) "1003" 127.0.0.1:6379> EXPIRE filter:result 300 (integer) 1
社交场景下的共同好友统计也是经典用法。把每个用户的好友存成一个集合,两个用户的共同好友只需一次SINTERSTORE即可得到,结果可直接用于推荐系统的候选集。如果需要多个维度的组合筛选,可以把SINTERSTORE的结果继续作为下一次计算的输入,实现链式集合运算,这也是SINTER比不了的,SINTER的结果回到客户端后想再参与服务端计算就必须重新写入,白白多了一次网络往返。
最后提醒两点:第一,Redis集群模式下,SINTERSTORE要求所有源键和目标键位于同一个哈希槽,通常需要通过hash tag(键名中用大括号包裹相同部分,如{user1}:friends与{user2}:friends)来保证,否则命令会报错。第二,参与计算的键如果存在非集合类型,会直接返回WRONGTYPE错误,使用前最好确认数据类型,或者在写入时做好类型约束,避免线上出现难以定位的异常。
RedisSINTERSTORE集合交集修改时间:2026-09-14 09:43:09