万级数据写入应优先使用 bulkWrite 而非 insertOne 循环或默认 insertMany,因其可大幅减少网络往返、避免频繁刷盘与 page split;推荐分批大小为 1000–5000,起始设为 2000 并依监控动态调整。

直接用 bulkWrite,别碰 insertOne 循环,也别默认用 insertMany —— 万级数据写入慢,90% 是因为没拆批、没控参、没关索引。
为什么 insertOne 循环在万级数据下必然卡死
每调一次 insertOne,就发一次网络请求、走一次完整服务端流程(解析 → 校验 → 写 oplog → 刷盘 → 返回),其中网络往返(RTT)占耗时 70% 以上。写 1 万条,就是 1 万次 RTT;按平均 5ms 延迟算,光等网络就花掉 50 秒。更糟的是,高频小写入会触发 WiredTiger 频繁 page split 和 journal 刷盘,I/O 毛刺明显,CPU 和磁盘负载虚高。
实操建议:
- 彻底禁用
for ... insertOne()模式,哪怕只写 100 条也不要用 - 如果必须逐条逻辑处理(如校验后才决定是否插入),先 collect 到内存 list,再批量提交
- 确认驱动版本:Node.js 驱动 v4.0+、pymongo v3.12+ 才对
bulkWrite错误返回支持完整
bulkWrite 分批大小怎么设:1000 还是 5000?
批次太小(如 100),起不到压缩网络开销的作用;太大(如 10000+),可能触发 WiredTiger 内存压力、连接超时或服务端 maxWriteBatchSize 截断(默认 100000,但客户端常提前拆分)。真实压测中,1000–5000 是吞吐与稳定性平衡点。
实操建议:
- 起始用
batch_size = 2000,观察 mongostat 中netIn和opcounters.bulk变化 - 若出现
SocketTimeoutError或Network timeout,立刻降到 1000 - 若
bulkWrite返回里writeConcernError频繁,说明副本集节点响应不均,需检查从节点 oplog lag,而非调大 batch - 不要用
Math.ceil(total / 10)这类公式硬套——实际应按单批耗时控制:目标单批执行
ordered: false 不是加速开关,而是错误处理开关
bulkWrite 默认 ordered: true:遇到第一个错误(如重复键、类型冲突),后续所有操作全跳过。这在订单创建等强一致链路里合理,但在日志、埋点、ETL 场景下等于主动放弃 99% 成功数据。
设为 ordered: false 后,MongoDB 并行执行所有操作,失败项不影响其余;但它不会抛异常,也不会中断流程——错误被静默记录在返回值的 writeErrors 字段里。
实操建议:
- 日志/埋点类写入,必须设
ordered: false,否则一条脏数据就能阻塞整批 - Python pymong 中要显式传
ordered=False,Node.js 驱动需写{ ordered: false },Go 驱动用options.BulkWrite().SetOrdered(false) - 每次
bulkWrite后必须检查result.writeErrors,打印或落库归档,否则失败完全不可见 - 在分片集群中,
ordered: false能让 mongos 并发投递到多个分片;ordered: true是串行路由,天然瓶颈
索引和 writeConcern 是隐藏性能杀手
往已有千万级数据的集合插新数据,如果该集合带多个索引,每条插入都要更新所有索引树。随机写入(如 UUID、哈希字段)还会导致索引页频繁分裂、缓存命中率暴跌。同时,writeConcern: { w: "majority" } 在副本集里会让整个批量卡在等待多数节点确认上——一个延迟高的从节点,就能拖垮整批。
实操建议:
- 导入前用
db.collection.getIndexes()查索引,非必要索引临时删掉:db.collection.dropIndex("xxx") - 导入完成再重建:
db.collection.createIndex({ field: 1 }, { background: true }),避免阻塞 - 日志类数据一律用
writeConcern: { w: 1 },别依赖默认——生产环境配置可能覆盖全局默认值 - 关键业务数据才用
w: "majority",且必须确保所有从节点oplogSize≥ 24 小时变更量 - 避免在同一批
bulkWrite里混用insertOne和updateOne(后者需先查),拉长整体耗时
真正卡住万级写入的,往往不是代码写法,而是索引没关、writeConcern 没降级、错误没检查——这些点一漏,bulkWrite 就退化成“看起来批量,实则单条”。



















