insertMany 默认 ordered: true 会因重复键中断插入,需设 { ordered: false } 并检查 writeErrors;唯一索引是防重复的唯一原子手段,建索引须 { unique: true } 且先清理重复数据;bulkWrite 更适合混合操作,避免滥用 upsert;批次大小应按 BSON 体积(≤16MB)而非条数控制,注意 _id 类型一致性。
默认 ordered: true 意味着只要某条文档触发唯一索引冲突(比如 E11000 duplicate key),后续所有文档立刻停止插入。这不是性能问题,而是行为设定——你看到“只插了一半”,其实是被主动截断了。
{ ordered: false } 才能让其余文档继续执行writeErrors 字段里,得手动检查try/catch 包整个 insertMany 来判断成败:即使有 99 条失败,result.ok 还是 1
靠应用层“先查再插”或 upsert 都扛不住并发竞态,两个请求同时没查到,就都插进去了。只有存储引擎层的唯一索引能真正拦住。
{ unique: true },例如:db.users.createIndex({ email: 1 }, { unique: true })
db.users.aggregate([ { $group: { _id: "$email", count: { $sum: 1 } } }, { $match: { count: { $gt: 1 } } } ])
null 多次插入,都会让索引失效;需要配 collation 或改用 sparse: true
如果你既要插新文档,又要更新部分字段,bulkWrite 是更清晰的选择;但千万别为了“跳过重复”而滥用 upsert: true。
upsert 本质是“查 + 插/改”,每次多一次索引查找,性能比纯 insertOne 差$set 或 filter 错误,可能把整条文档覆盖成空对象bulkWrite + insertOne + { ordered: false },语义干净、开销最小result.writeErrors[i].index,对应原始数组下标,不是插入后顺序insertMany 提交的是一个 BSON 数组,总大小不能超 16MB。单条文档 1KB,1.5 万条就满了;如果文档长短不一,硬按 10 万条切批,大概率踩中 Document too large for insert。
Buffer.byteLength(JSON.stringify(doc), 'utf8'),再加 20% 余量JSON.parse(fs.readFileSync(...)) 加载全部数据,用 stream.Transform 边读边 chunkmaxWriteBatchSize(10 万)拆批,但这是上限不是建议值;生产环境推荐 ≤5 万条/批_id 类型一致性——字符串 "507f1f77bcf86cd799439011" 和 ObjectId("507f1f77bcf86cd799439011") 在索引眼里是两个值,看着重复,其实全都能插进去。