bulkwrite是mongodb高效写入的核心手段,可将千次独立写入压缩为一次请求,实测吞吐提升50–100倍;其性能优势源于消除重复网络开销(占70%以上)与优化i/o连续性,需合理配置ordered、批次大小(1000–5000)、writeconcern及错误处理机制。

bulkWrite 是 MongoDB 实现海量数据高效写入最直接有效的手段,它能把成百上千次独立写入压缩为一次网络请求,实测吞吐量可提升 50–100 倍。关键不在于“能不能用”,而在于“怎么分批、怎么配参、怎么防崩”。
为什么单条 insertOne 会拖垮写入性能
每调用一次 insertOne,客户端就要发一次网络请求、服务端要走一次完整写入流程(解析、校验、写 oplog、刷盘、返回),其中网络往返开销占总耗时 70% 以上。当你要写入 10 万条日志,就是 10 万次 RTT —— 即便延迟只有 2ms,光网络就卡住 200 秒。
更隐蔽的问题是:高频小写入会加剧 WiredTiger 的 page split 和 journal 刷盘频率,导致 I/O 毛刺和锁争用上升。
使用 bulkWrite 后,这些操作被合并进一个请求体,服务端批量解析、原子执行,网络开销归零,I/O 更连续。
bulkWrite 的两种模式:ordered=false 不是万能加速键
bulkWrite 默认按 ordered: true 执行:遇到第一个错误就中止,后续操作全跳过。适合强一致性场景(如订单创建链路)。
设为 ordered: false 时,MongoDB 会并行尝试所有操作,失败项不影响其余项。这对日志、埋点、ETL 等容忍部分失败的场景极有用,但要注意:
- 错误不会中断流程,但你需要主动检查返回结果里的
writeErrors字段,否则失败会被静默吞掉 - 在分片集群中,
ordered: false能让mongos并发投递给多个分片,否则默认串行路由 - 不是所有驱动都默认支持无序模式,Node.js 驱动需显式传参,Python 的
pymongo需加ordered=False
批次大小怎么定:1000–5000 是安全区,但得看数据体积
批次太小(如 100 条):没省多少网络开销,还增加客户端内存压力;批次太大(如 20000 条):可能触发 BSON 16MB 限制、OOM、或让单次执行时间过长,阻塞其他请求。
推荐策略:
- 单文档平均大小 ≤ 1KB → 批次设为 5000
- 单文档平均大小在 1–5KB → 批次设为 1000–2000
- 含二进制字段(如 base64 图片)→ 强烈建议先压缩再入库,并把批次压到 500 以下
- 永远用
try/catch包裹bulkWrite调用,捕获MongoBulkWriteError或驱动对应异常类型
示例(Node.js):
await collection.bulkWrite(operations, {
ordered: false,
writeConcern: { w: 1 }
});
writeConcern 和 journal 配合调优:w:0 不等于“裸奔”
默认 writeConcern: { w: 1 } 表示只等主节点确认,已平衡安全与速度。若你写的是临时日志、监控指标等可丢数据,可设 w: 0 —— 它不等待任何响应,连错误都不报,真正“发完即弃”。
但注意:w: 0 不代表禁用 journal,journal 控制的是崩溃恢复能力。若你同时关 journal(启动时加 --nojournal 或配置 storage.journal.enabled: false),那断电就真丢数据。
生产环境更常用的是折中方案:
- 高吞吐 + 可接受秒级丢失 →
w: 1, j: false - 强一致要求(如金融类)→
w: "majority", j: true - 写入瓶颈在磁盘?优先换 SSD,而不是盲目关 journal
真正容易被忽略的点是:writeConcern 是 per-operation 设置,bulkWrite 的每个子操作可以单独指定,但通常统一设更可控。











