mongodb 6.0 副本集中 $unionwith 可用,但要求所有节点 fcv 设为 "6.0",否则 pipeline 被忽略或报错;合并多集合需链式调用,每个阶段显式写 pipeline: [],并注意读偏好、索引与排序内存限制。

在 MongoDB 6.0 副本集中直接用 $unionWith 合并多集合完全可行,但必须确认主节点和所有从节点的 featureCompatibilityVersion 已设为 "6.0",否则 pipeline 参数会失效或报错。
为什么副本集里 $unionWith 有时不生效
不是语法写错了,而是副本集启用了旧版兼容模式。MongoDB 6.0 要求所有节点的 featureCompatibilityVersion 至少为 "6.0",否则即使版本号显示是 6.0,$unionWith 的 pipeline 字段也会被忽略,甚至触发 "unrecognized field 'pipeline'" 错误。
检查方式:db.adminCommand({getCmdLineOpts: 1}) 看 parsed.featureCompatibilityVersion;升级方式:db.adminCommand({setFeatureCompatibilityVersion: "6.0"})(需在主节点执行,且所有节点在线)。
- 从节点未同步完成 FCV 升级时,聚合可能在从节点上失败(尤其带
readPreference=secondary) - 如果副本集里混着 5.0 和 6.0 节点,FCV 必须设为最低共同版本(即
"5.0"),此时$unionWith的pipeline仍不可用 -
$unionWith本身不依赖分片逻辑,所以在副本集里行为和单机一致——它只关心 FCV 和权限
合并三个及以上集合的写法要点
$unionWith 每次只能接一个集合,要合并多个,得链式调用。不能写成数组传入,也不能嵌套;必须展开为多个连续阶段。
例如合并 logs_q1、logs_q2、logs_q3:
db.logs_q1.aggregate([
{ $unionWith: { coll: "logs_q2", pipeline: [] } },
{ $unionWith: { coll: "logs_q3", pipeline: [] } },
{ $sort: { timestamp: 1 } }
])
- 每个
$unionWith都要显式写pipeline: [],空数组不是可选——省略会导致隐式去重(尤其在低 FCV 下更危险) - 顺序不保证,所以最终
$sort不可省;若字段名不统一(如tsvstimestamp),必须在各自pipeline内用$project对齐 - 不要试图在某个
$unionWith的pipeline里再嵌$unionWith——语法不支持,会报"$unionWith is not allowed inside a pipeline"
读取偏好(readPreference)对结果的影响
在副本集里,默认 primary 读取没问题;但如果用了 secondary 或 nearest,要注意:$unionWith 是聚合阶段,其输入集合的读操作受读取偏好影响,但合并动作总是在接收请求的节点(即你连的那个 mongod)上完成。
- 连的是从节点 +
readPreference=secondary:所有$unionWith的源集合都从该从节点拉取——但该从节点不一定有全部集合的最新数据(取决于复制延迟) - 连的是主节点 +
readPreference=primaryPreferred:行为稳定,推荐生产环境默认用这个 - 千万别在
$unionWith后紧跟$lookup关联另一个集合——如果那个集合不在同一副本集节点上(比如跨数据库或视图),会报"cannot run $lookup on non-local collection"
性能卡点:排序内存溢出与索引缺失
$unionWith 合并后若立即 $sort,而没在 $sort 字段上建索引,整个中间结果会在内存中排序,极易触发 "Sort exceeded memory limit (104857600 bytes)" 错误。
- 解决办法只有两个:一是在
$sort字段(如{ timestamp: 1 })上为所有参与集合建相同索引;二是改用应用层分页归并(查各集合 top N,再在代码里 merge sort) - 副本集的 oplog 复制不影响
$unionWith性能,但它会影响你看到的数据新鲜度——如果从节点延迟 5 秒,$unionWith从它读出来的就是 5 秒前的数据 - 别在
$unionWith后加$group统计总数再$limit——这会让全量数据进内存;真要分页,优先用多次独立查询 + 应用层 offset/limit
最常被忽略的一点:副本集里 $unionWith 的错误不会自动 fallback 到其他节点重试,它就卡在当前执行节点上。所以 FCV 不一致、索引缺失、字段类型错(比如字符串当数字求和),都会直接报错返回,而不是静默跳过或降级处理。











