SINTER耗时源于单线程遍历最小集合并逐个哈希查找,时间复杂度O(N×M);SUNION需全量构建新集合,易阻塞主线程;intset编码下查找为O(log N),反不如hashtable的O(1)高效。

交集命令 SINTER 的实际耗时来源
交集不是“查一下两个集合共同元素”那么简单。SINTER 会把所有参与运算的集合全量加载进内存,再逐个比对成员是否存在。这意味着:如果第一个集合有 10 万元素,第二个有 5 万,Redis 实际要做最多 10 万次 SISMEMBER 查询(底层哈希表 O(1) 查找),但整个过程是单线程串行执行的,无法并发加速。
更关键的是——Redis 总是选最小的集合做遍历基准,其余集合用哈希表查。所以集合大小差异越大,实际耗时越接近小集合的元素数 × 哈希查找开销;但如果所有集合都很大(比如都超 50 万),哪怕只是求交,也容易卡住主线程 100ms+,触发慢日志告警。
- 不要在高并发路径上直接调用
SINTER key1 key2 key3,尤其当任意一个 key 可能动态增长到 10 万以上 - 若业务允许近似结果,改用客户端计算:先用
SSCAN分批拉取各集合子集,在应用层做交集(牺牲一致性换响应时间) - 提前预判规模:对高频参与交集的 key,用
SCARD定期采样,发现超过阈值(如 5 万)就走降级逻辑
并集命令 SUNION 的内存与阻塞风险
SUNION 看似只是合并去重,但它必须构造一个全新集合返回,这个新集合要一次性分配内存、写入全部不重复元素。当输入集合总元素量达百万级时,不仅内存峰值飙升,还可能触发 Redis 主线程长时间阻塞——因为整个过程不可中断、不可分片。
尤其危险的是:如果其中一个输入 key 是个“大 Set”,而你又没设超时或熔断,一次 SUNION 就可能拖慢整个实例的后续请求。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 永远避免在定时任务或用户请求链路中无保护地调用
SUNION,尤其是带通配符或动态 key 的场景 - 用
SUNIONSTORE替代SUNION:把结果存到新 key,避免把大结果传回客户端,减少网络和内存压力 - 如果只是需要“是否存在任一集合包含某成员”,别用
SUNION+SISMEMBER,直接循环调用SISMEMBER更快、更可控
intset 和 hashtable 切换对集合运算的影响
很多人以为“小整数集合更快”,但交并差运算时恰恰相反:SINTER / SUNION 在 intset 上反而更慢。因为 intset 是有序数组,查找靠二分,O(log N);而 hashtable 是 O(1)。当集合自动从 intset 升级为 hashtable 后,交并操作才真正达到常数级查找效率。
但升级不可逆——哪怕你后来删光所有非整数,它也不会退回 intset。这就导致一个隐蔽问题:早期用整数建的 Set,随着业务加字符串成员,悄悄变慢了,监控却看不出异常。
- 用
OBJECT ENCODING key检查关键 Set 的编码类型,确认是否已转为hashtable - 如果确定只存整数且规模可控(
set-max-intset-entries默认 512),可适当调高该配置,延长 intset 阶段,节省内存;但别指望它提升交并性能 - 对核心业务的 Set,上线前用真实数据压测
SINTER耗时,别只看SCARD数值
替代方案比优化命令更有效
官方文档不会明说:很多场景下,硬扛 SINTER 或 SUNION 是错的。Redis 的单线程模型决定了复杂集合运算天然不适合实时高并发。
真正稳定的解法,往往是绕开它:把交/并逻辑下沉到写入阶段,或者用 Sorted Set + score 做预计算。比如“共同好友”不现场算,而是在关注关系变更时,用 ZADD 维护一个“双向关系分值”,查询时 ZRANGEBYSCORE 直接拉。
- 交集需求 → 考虑用布隆过滤器(Bloom Filter)预筛,大幅减少实际参与
SINTER的 key 数量 - 并集需求 → 如果只是判断“属于任一标签”,改用 HyperLogLog 做概率去重,或拆成多个小
SISMEMBER并行调用 - 任何线上服务,只要看到慢日志里反复出现
SINTER或SUNION,第一反应不该是调优,而是问:这个计算真的必须在 Redis 里实时做吗?


















