SUNION 合并推荐列表易漏权重因它丢弃分值、结果无序且无法区分行为时效性,仅适用于无权重的静态集合合并;多源合并时还存在阻塞、静默丢数据等问题。

为什么用 SUNION 合并推荐列表容易漏掉权重信息
直接用 SUNION 把多个用户行为集合(比如 user:123:liked、user:123:searched)合并,确实能拿到并集,但所有元素一律平等——刚点开的视频和三个月前搜过的词,在结果里没区别。这不是“推荐”,是“罗列”。
实操建议:
- 别把原始行为集合直接丢给
SUNION;先用ZSET存带分值的行为,比如ZADD user:123:behavior 8.5 "video:456",热度、时效、点击深度都转成 score - 真要用
SUNION,只适合做兜底或冷启动:比如合并“热门榜”global:trending和 “新上架”catalog:new这类本身无个体权重的静态集合 -
SUNION结果是无序的,后续还得SRANDMEMBER或加一层SMEMBERS+ 应用层 shuffle,否则每次返回顺序固定,推荐多样性归零
SUNION 在多源实时推荐中性能卡在哪
当你要合并 5 个活跃用户的最近行为集合(每个含 200+ 元素),SUNION key1 key2 key3 key4 key5 看似一行搞定,但 Redis 是单线程执行命令,大集合合并会阻塞其他请求。更糟的是,如果某个 key 不存在,SUNION 默认当成空集处理,表面成功,实际悄悄吞掉一整个数据源。
实操建议:
- 用
EXISTS预检关键 key 是否存在:EXISTS user:123:cart,避免“静默丢失” - 控制参与
SUNION的 key 数量,超过 3 个就拆成两轮:SUNION key1 key2→STORE temp:union1,再SUNION temp:union1 key3,减少单次阻塞时长 - 对高频调用场景,改用应用层合并:用 pipeline 批量
SMEMBERS拿各集合,在内存去重合并,Redis 只承担存储角色
替代 SUNION 的轻量方案:用 SADD + SRANDMEMBER 做动态拼接
如果你要的是“从 A 拿 3 个、从 B 拿 2 个、从 C 补 1 个”,而不是严格数学并集,硬套 SUNION 反而绕路。尤其当各来源数据量差异大(比如“猜你喜欢”有 50 个,“好友在看”只有 4 个),SUNION 会把小集合完全淹没。
实操建议:
- 按优先级分批取:
SRANDMEMBER user:123:rec_favorites 3、SRANDMEMBER user:123:rec_friends 2,应用层拼数组再打乱 - 用
SADD构建临时推荐池:SADD rec:temp:123 itemA itemB,再SRANDMEMBER rec:temp:123 5,比反复SUNION更可控 - 注意
SRANDMEMBER默认不删除元素,如需“抽过即失效”,得配合SPOP,但要承担并发重复抽取风险
线上出问题时怎么快速定位 SUNION 是否背锅
用户反馈“推荐列表突然变短”或“全是老内容”,第一反应别急着查算法,先确认是不是 SUNION 调用链断了。常见错误现象包括:返回空数组、结果数量远少于各源之和、相同输入每次返回不同元素(说明某 key TTL 到期被删了)。
实操建议:
- 用
DEBUG OBJECT key_name查 key 类型和内存占用,确认不是误把ZSET当SET传给了SUNION(会报错WRONGTYPE Operation against a key holding the wrong kind of value) - 用
SCAN检查相关 key 是否存在且非空:EXISTS user:123:liked+SCARD user:123:liked - 在业务日志里埋点记录每次
SUNION的输入 key 和输出数量,异常值(如输出为 0 但输入 key 都存在)立刻告警
真正难处理的不是 SUNION 本身,而是它掩盖了上游数据写入失败——比如行为采集服务挂了两小时,所有 user:*:recent 集合都空了,SUNION 还是安静地返回空集,没人发现。










