mongodb聚合结果单文档超16mb触发bsonsizeexceededexception,是最终输出文档而非中间过程超限;$group+$push或深层$lookup易导致膨胀;唯一解法是$unwind、$facet或$bucketauto拆分文档。

聚合结果单文档超过16MB触发BSONSizeExceededException
不是管道中间过程出错,而是最终输出的某一个文档(哪怕只有一条)体积超了16777216字节——MongoDB在序列化响应时直接拦截,报BsonMaximumSizeExceededException或Document exceeds maximum size of 16777216 bytes。这个限制是BSON协议层硬编码的,allowDiskUse: true完全无效。
$group + $push、$lookup嵌套过深是高频诱因
常见于两类写法:
-
$group后用$push收集大量子文档:比如按user_id分组,把万条日志全塞进logs: {$push: "$$ROOT"} -
$lookup关联出巨量嵌套数据:比如主表一条订单,关联的items数组有5000项,每项带图片URL和描述字段
这些操作会让单个输出文档瞬间膨胀,$project删字段必须放在$group或$lookup之后、但要在最终返回前——顺序错了就白加。
别信“中间文档可超限”,落地时照样崩
虽然聚合管道内部允许临时文档超16MB(比如$unwind展开后暂存),但只要最终要返回、写入集合或触发$out,MongoDB就会校验每个文档的BSON大小:
- 用
$out写入集合?目标集合每条文档仍受16MB限制 - 用
$merge?同样校验单文档大小,不通过就报错 - 返回游标?服务端流式吐出,但若某条结果文档本身超限,客户端收第一个
next()就失败
真正绕开16MB墙的唯一方式,是把大结果拆成多条小文档:$unwind展开数组、或用$facet分流聚合逻辑、或改用$bucketAuto分段统计。
查不出问题?先用Object.bsonSize()实测
别猜哪条文档超限,直接在mongo shell里抓样例测:
db.collection.aggregate([
{ $match: { status: "error" } },
{ $limit: 1 }
]).toArray().forEach(doc => print(Object.bsonSize(doc)))
输出接近16777216就是它。聚合管道里也得盯住关键阶段输出大小,加explain("executionStats")看executionTimeMillisEstimate突增点——那往往是文档开始膨胀的位置。
最容易被忽略的是:你看到的“超限”错误,90%不是数据总量太大,而是某个$group键下恰好聚了一堆大字段,或者$lookup没加$project提前裁剪关联集合字段。











