MongoDB 4.0 不支持跨分片事务,因此事务内任何聚合操作(如含 $lookup、$facet 或写入阶段)均失败;仅当聚合作用于单一分片且无跨集合/写入操作时才可能成功。

MongoDB 4.0 不支持跨分片事务中的聚合操作,任何涉及多个分片的聚合(尤其是含 $lookup、$facet 或写入阶段)在事务内直接报错。 这不是配置问题,而是 4.0 根本没有跨分片事务能力 —— 它连跨分片事务都做不到,更别说在其中跑聚合了。
为什么 $lookup 在事务里一用就失败?
在分片集群中,$lookup 若关联的集合未共置(即两个集合的分片键不同、或其中一个未分片),MongoDB 会尝试路由到多个分片执行。而 4.0 的事务只允许所有操作落在同一分片上,一旦检测到跨分片访问,立即抛出 TransactionNumbersMismatch 或 NoSuchTransaction。
- 错误典型场景:
db.orders.aggregate([{$lookup: {from: "customers", ...}}]),其中orders按userId分片,customers按_id分片 → 必然失败 - 即使两个集合用了相同分片键,若未显式保证共置(如未用
sh.shardCollection对两集合分别指定相同分片键 +collation),$lookup仍可能被拆分到不同分片 -
$lookup在事务中不支持pipeline参数含写操作(如$addFields后接$out),$out本身就被明确禁止在事务中使用
aggregate() 能否在 4.0 事务中安全使用?
仅当满足全部下述条件时,aggregate() 才可能在事务中执行成功:
- 目标集合是分片集合,且所有 pipeline 阶段的查询条件都包含该集合的分片键等值匹配(例如
{userId: "u123"},且userId是分片键) - 不含任何写入阶段:
$out、$merge、$geoNear(部分版本)、mapReduce均被拒绝 - 不含跨集合操作:
$lookup、$unionWith必须关闭,或确保被查集合与主集合物理共置在同一分片(需提前验证sh.status()中的 chunk 分布) - 整个 pipeline 输出结果不超过 16MB(事务级大小上限),且单次聚合不触发分片间数据移动(如
$group后$sort可能引发内存溢出并中止)
替代方案:绕过 4.0 的跨分片聚合限制
与其硬扛 4.0 的架构天花板,不如按实际数据流重构:
- 把聚合逻辑前置:在应用层先查出主集合文档(带分片键),再按 key 并行查关联集合(同样带分片键),最后内存组装 —— 避免事务内跨分片
- 用物化视图代替实时聚合:在业务低峰期用后台 job 将常用
$lookup结果写入新集合(注意:不能在事务中建这个集合,需提前创建并分片) - 升级到 4.2+ 并启用分布式事务:但必须接受代价 ——
config.transactions集合依赖、WiredTiger强制要求、吞吐下降 30%~50%,且$lookup仍需共置保障 - 放弃事务包装聚合:如果聚合结果只读、不参与后续写操作,就别塞进事务 —— 用
readConcern: "majority"+ 快照时间戳也能获得强一致性读
最常被忽略的一点:db.adminCommand({setFeatureCompatibilityVersion: "4.2"}) 不会自动让 4.0 集群支持跨分片事务。它只是升级前提,真正的分片事务能力从 4.2 开始才存在,且默认关闭。别在 4.0 上调 startTransaction() 后还指望 $lookup 跑通 —— 它连事务都启不来。


















