$lookup不能直接跨数据库关联,因其from参数仅支持字符串集合名,不解析JavaScript表达式;必须分两步:先在源库用$out导出数据到临时集合,再在目标库用$lookup关联该临时集合。

为什么$lookup不能直接跨数据库做关联?
因为 $lookup 的 from 参数只接受字符串集合名,不支持 db.getSiblingDB("other_db").collection 这类表达式。你写进去会报错 "from" must be a string,不是语法错误,是设计限制。
常见误操作是试图在聚合里直接引用另一个库的集合,比如:
db.orders.aggregate([
{ $lookup: { from: "db.getSiblingDB('base_data').mobile_segment", ... } }
])
这根本不会执行——from 字段压根不解析 JS 表达式。
跨库同步必须分两步:先导出再关联
核心思路是把「跨库」问题拆成「同库内操作」:先切到源数据库,把目标数据聚合后 $out 到一个临时集合;再切回目标库,用 $lookup 关联这个临时集合。
实操要点:
- 第一步(在源库):
use base_data,运行db.mobile_segment.aggregate([ ..., { $out: "mobile_segment_temp" } ]) - 第二步(切回目标库):
use system_logging,再跑主聚合管道,from: "mobile_segment_temp" - 临时集合名要确保唯一、无歧义,建议加时间戳或业务前缀,比如
mobile_segment_temp_20260907 - 临时集合不需要手动删,但长期堆积会影响磁盘和命名空间管理,建议同步完成后
db.mobile_segment_temp_20260907.drop()
$merge 阶段写入目标集合时的关键约束
$merge 是真正落地更新的阶段,但它对目标集合有硬性要求:
-
on: "_id"字段必须是目标集合已有的唯一索引字段,否则报错"on" field must resolve to a unique index - 目标集合如果不存在,
$merge不会自动创建(除非你显式指定into: { db: "...", coll: "..." }且该数据库已存在) -
whenMatched: "replace"会整条替换文档,丢失原集合中未被管道输出的字段;更安全的是用"merge"模式,只覆盖管道中明确输出的字段 - 如果目标库是分片集群,
into指定的数据库必须已存在,否则失败;单机或副本集则会自动创建
性能与权限容易被忽略的点
跨库同步本质是两次读+一次写,I/O 和锁开销比单库高得多。尤其要注意:
- 临时集合最好建在与目标库同一物理节点上(比如都放在主节点),避免网络传输放大延迟
- 运行
$out需要insert+dropCollection权限;$merge需要insert+update+find权限,缺一不可 - 聚合管道中
$lookup关联临时集合时,务必给该集合建好索引(比如localField对应字段),否则全表扫描会让同步卡死 - 大集合同步建议加
allowDiskUse: true,防止内存超限中断;但也要注意磁盘空间是否足够存中间结果
跨数据库同步不是“配置一下就能跑”,每一步的中间状态(临时集合是否存在、索引是否生效、权限是否收敛)都得亲手验证,漏掉任一环都会静默失败或数据错乱。


















