insertmany 插入百万数据慢或中断,根本原因是默认 ordered: true、writeconcern: { w: "majority" } 和单批超 16mb bson 限制三者叠加所致;需显式设 ordered: false、writeconcern: { w: 1 } 并按字节安全分批。

insertMany 本身不慢,慢是因为默认 ordered: true + 默认 writeConcern: { w: "majority" } + 单批超限 —— 这三者叠加会让百万级插入卡在“看起来很慢”甚至“只插了一半就停了”的假象里。
为什么 insertMany 插入百万数据会中断或极慢?
常见错误现象:BulkWriteError 只报第一个失败项,后续文档全丢;耗时远超预期但没报错;日志里反复出现 Document too large for insert。
-
ordered: true(默认)下,任意一条文档冲突(如重复_id或唯一索引冲突),后续所有文档直接跳过,不是“慢”,是“提前终止” - 默认
writeConcern: { w: "majority" }要等多数节点确认,网络延迟高时每批都卡住,尤其跨机房部署 - 单次传入数组总 BSON 大小超 16MB(不是单条限制),驱动不会自动拆分,直接抛
Document too large - 驱动内部按
maxWriteBatchSize = 100000自动分批,但这是条数上限,不是字节上限 —— 文档长度不均时仍可能触发 16MB 限制
如何设置 insertMany 参数绕过默认陷阱?
关键不是“怎么写”,而是“怎么配”。必须显式覆盖三个默认值:
- 加
{ ordered: false }:跳过失败项,继续插其余文档;适合清洗前的原始数据导入 - 设
{ writeConcern: { w: 1 } }:只等主节点写入,避免多数节点同步等待;若需强一致性,再调高w值 - 禁用
bypassDocumentValidation: true(仅当已确保数据合规):跳过服务端 schema 验证,省掉一轮解析开销
示例(Node.js 驱动):
collection.insertMany(docs, {
ordered: false,
writeConcern: { w: 1 },
bypassDocumentValidation: true
});
怎么安全拆分百万数据,避开 BSON 16MB 限制?
别按固定条数切(比如每 10000 条一批),要按字节估算。因为一个含长文本或嵌套数组的文档可能占几百 KB,而另一条只有几个字段才几十字节。
- 用
Buffer.byteLength(JSON.stringify(doc), 'utf8')预估单条体积,留 20% 余量后算出每批最大字节数(比如 12MB) - 遍历文档数组累加字节,达到阈值就切一批,比硬分页更可靠
- 大数据源(如文件流、数据库游标)直接用
stream.Transform边读边 chunk,不把全部数据 load 到内存 - 注意:即使你手动分批,每批仍要遵守
maxWriteBatchSize ≤ 100000,否则驱动会二次拆分并可能报错
insertMany 后怎么知道哪些成功、哪些失败?
返回值里的 result.insertedCount 和 result.insertedIds 在不同驱动版本行为不一致(v4+ 才稳定支持 insertedIds),不能直接靠它做业务判断。
- 插入前给每条文档加临时标记,如
tempId: Date.now() + '-' + Math.random() - 失败后从
result.getWriteErrors()提取index字段,反查原数组对应项 - 不要插入完立刻
find({ _id: xxx })查刚插的 ID —— 网络延迟或写关注级别低时可能查不到;应统一用find({ _id: { $in: insertedIds } })批量确认 - 事务内用
insertMany要格外小心:任一文档失败 → 整个事务回滚,ordered: false失效
真正卡住百万插入的,往往不是代码写法,而是对 ordered、writeConcern 和 BSON size 的误判 —— 这三处不动,光换写法或加并发,效果有限。











