insertMany默认ordered:true导致中间失败即中断,非本身慢;应设{ordered:false}跳过错误项,调优writeConcern和分批(≤10万条/批、总BSON≤16MB)以提升性能。
不是 insertMany 本身慢,而是默认开启了有序执行(ordered: true),一旦中间某条文档插入失败(比如重复键冲突),后续所有文档都会被跳过。这在调试时容易误判为“批量失败”,实际只是被中断了。
常见错误现象:BulkWriteError 报错但只看到第一条失败原因,其余插入结果不可见;或数据量大时耗时远超预期——往往是因为没关掉写关注(writeConcern)或没调优连接池。
{ ordered: false } 允许跳过失败项继续执行,适合脏数据容忍场景writeConcern: { w: 1 } 可避免默认的 { w: "majority" } 带来的同步等待insertMany 不宜超过 10 万条文档,否则可能触发 BSON 限制(16MB)或内存溢出insertMany 提交的整个数组会被序列化为一个 BSON 对象,总大小不能超过 16MB。这不是每条文档限制,而是整批请求上限。遇到 Document too large for insert 就得拆分。
实操建议:
Buffer.byteLength(JSON.stringify(doc), 'utf8') 预估单条体积,留 20% 余量后计算批次大小chunkSize 硬拆——按字节而非条数切更可靠,尤其文档长度差异大时stream.Transform 边读边 chunk,避免一次性加载全部数据到内存insertMany 返回值里的 ops 数组和原始输入顺序一致,但只有成功插入的文档才有 _id 字段。如果用了 { ordered: false },失败项不会出现在 ops 中,但你无法直接对应到原数组索引。
正确做法:
tempId: Math.random()),失败后从 result.getWriteErrors() 中提取 index 找回原数据find({ _id: { $in: insertedIds } }) 二次确认result.insertedCount 和 result.upsertedCount 的含义有差异,v4+ 才统一支持 insertedIds 字段可以,但必须确保所有文档都满足事务约束(如唯一索引、引用完整性),且整个事务内操作总大小仍受 16MB 限制。更大的风险在于:事务中 insertMany 失败会导致整个事务回滚,无法像非事务模式那样用 ordered: false 局部容错。
使用场景有限:
insertMany 和大量 updateOne,容易超时或锁表真正影响效率的从来不是方法名,而是你有没有在插入前删掉那个没用的 ensureIndex 调用,或者忘了关掉日志级别的 slowms 设置。