16MB 的上限到底卡在哪一步?嵌套深度的上限是多少?这篇构造刚好 16 MiB 的文档,逐字节增加,看 insert、update 和聚合在哪一步报什么错,并用二分查找确认嵌套深度的上限。
test> h = db.hello(); ({maxBsonObjectSize: h.maxBsonObjectSize, maxMessageSizeBytes: h.maxMessageSizeBytes, maxWriteBatchSize: h.maxWriteBatchSize})
{
maxBsonObjectSize: 16777216,
maxMessageSizeBytes: 48000000,
maxWriteBatchSize: 100000
}
maxBsonObjectSize = 16,777,216 = 16 × 1024 × 1024hello 返回 maxBsonObjectSize 16,777,216 字节,整个 BSON 文档(含所有键名和结尾)不能超过它。
文档编码后 16,777,216 字节(= 1.0000 × 16 MiB)
insert_one 成功,耗时 31 ms;读回 len(s) = 16,777,194
多 1 字节(16,777,217): WriteError: object to insert too large. size in bytes: 16777217, max size: 16777216, full error: {'index': 0, 'code': 10334, 'errmsg': 'object to insert too large. size in 刚好 16 MiB 的文档能插入;多 1 字节服务端返回错误码 10334 object to insert too large。
文档 16,777,217 字节(超 1 字节): n=0, writeErrors=[{'index': 0, 'code': 10334, 'errmsg': 'object to insert too large. size in bytes: 16777217, max size: 16777216'}]
文档 16,778,240 字节(超 1024 字节): n=0, writeErrors=[{'index': 0, 'code': 10334, 'errmsg': 'object to insert too large. size in bytes: 16778240, max size: 16777216'}]
超 16 KiB: DocumentTooLarge: BSON document too large (16793685 bytes) - the connected server supports BSON document sizes up to 16793598 bytes.
集合里最后的文档数: 1(只有最开始刚好 16 MiB 的那一个)直接用 insert 命令时,超限文档在回复的 writeErrors 里,命令本身不抛异常;超出命令体上限(16793598)时驱动在客户端就拒绝。
起点文档约 14.0 MiB
test> try { db.grow.updateOne({_id: 1}, [{$set: {s: {$concat: ["$s", "$s"]}}}]) } catch (e) { e.message }
Plan executor error during update :: caused by :: RCString too large. Requires size=29360128 < limit=16777216
test> bsonsize(db.grow.findOne({_id: 1})) / 1024 / 1024
14.000020980834961
test> var r = null; for (var i = 0; i < 40; i++) { try { db.arr.updateOne({_id: 1}, {$push: {a: "x".repeat(1024 * 1024)}}); } catch (e) { r = "第 " + (i + 1) + " 次 $push 1MiB 报错: " + e.message; break } } r
第 16 次 $push 1MiB 报错: Plan executor error during update :: caused by :: Resulting document after update is larger than 16777216
test> db.arr.aggregate([{$project: {n: {$size: "$a"}}}])
[ { _id: 1, n: 15 } ]$push 把文档撑过上限时报 Resulting document after update is larger than 16777216,文档保持原样;聚合表达式里拼出过大的字符串则报 RCString too large。
内嵌 100 层: 成功;二分查找得到:内嵌 179 层成功,180 层失败(外层文档本身也算 1 层,即总深度 180 成功)
失败时的错误: cannot insert document :: caused by :: object exceeds 180 levels of nesting
内嵌 250 层(超过 BSON 解析上限):BSONObj exceeds maximum nested object depth in element with …
test> db.adminCommand({getParameter: 1, maxBSONDepth: 1})
{ maxBSONDepth: 200, ok: 1 }在这台 9.0.2 上,二分查找得到内嵌 179 层可以插入、180 层失败(object exceeds 180 levels of nesting);更深的文档连 BSON 本身都拒绝(参数 maxBSONDepth 是 200)。常说的 100 层与此不同,以实测为准。
test> r = db.parts.aggregate([{$group: {_id: "$g", all: {$push: "$s"}}}]).toArray()[0]; [r.all.length, bsonsize(r), bsonsize(r) > 16 * 1024 * 1024]
[ 20, 20971714, true ]
test> try { db.parts.aggregate([{$group: {_id: "$g", all: {$push: "$s"}}}, {$out: "outc"}]).toArray(); "ok" } catch (e) { e.message.slice(0, 200) }
Executor error during aggregate command on namespace: test.parts :: caused by :: Serializing Document failed :: caused by :: Size 20971714 exceeds maximum 16793600
test> db.parts.aggregate([{$group: {_id: "$g", n: {$sum: 1}, bytes: {$sum: {$strLenBytes: "$s"}}}}])
[ { _id: 1, n: 20, bytes: 20971520 } ]聚合的结果文档可以超过 16 MiB 返回给客户端(实测 20,971,714 字节),但写进集合($out)时超过 16793600 字节就失败。需要汇总时尽量用累加器而不是 $push 全部元素。