应优先使用bulkwrite而非insertmany或循环insertone,因其支持混合操作、per-operation writeconcern及ordered=false并发执行,实测万级数据写入可从200秒降至5秒内;需合理分批(100–500条/批)、显式配置writeconcern与timeoutms,并主动检查writeerrors。

直接用 bulkWrite,别用 insertMany 或循环 insertOne——万级数据写入从 200 秒压到 5 秒内,关键不在“用不用”,而在“怎么分批、怎么配参、怎么查错”。
为什么 bulkWrite 比 insertMany 更值得细调
insertMany 是 bulkWrite 的简化封装,它默认 ordered: true,不支持混合操作,也不能为单条操作设不同 writeConcern。真正需要差异化控制时,绕不开 bulkWrite:
- 想给一批用户补字段、另一批删记录?
insertMany做不到,必须用bulkWrite配合InsertOne和DeleteOne - 部分数据要等主从同步(
w: "majority"),部分只要主节点落盘(w: 1)?只有bulkWrite支持 per-operation 级别配置 - 误设
ordered: true后某条因唯一键冲突失败,整批后续全跳过——这种静默丢数据,在日志类场景里极难排查
批次大小怎么定:不是固定 1000,而是看 BSON 体积
硬套“每批 1000 条”容易翻车。根本约束是单个请求体不能超 MongoDB 的 BSON 16MB 上限:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
- 如果单条文档平均 8KB,1000 条就接近 8MB;若含 base64 图片或大文本,300 条就可能触发
DocumentTooLarge - 实测安全区间通常是 100–500 条/批:小于此数,网络节省不明显;大于此数,易触发服务端 OOM 或超时
- 别一次性构造全部
requests数组再传给bulkWrite,应边查边攒、攒够一批就发一批,避免客户端内存暴涨
ordered: false 不是加速开关,而是错误处理开关
设 ordered: false 后,MongoDB 并行执行所有操作,失败项不影响其余,但错误不会抛异常,而是塞进返回值的 writeErrors 字段里:
- 不主动读
result.writeErrors,就等于把失败当成功——ETL 或埋点写入中漏掉几百条失败文档,可能几周都发现不了 - 在分片集群中,
ordered: false才能让mongos并行投递到多个分片;ordered: true是串行路由,天然卡瓶颈 - PyMongo 和 Node.js 驱动默认都是
ordered: true,不显式传参就不是“无序”
writeConcern 必须按数据等级显式设,别信“默认”
副本集写入慢,80% 是因为 writeConcern 卡在等多数节点确认。比如设了 w: "majority",但某个从节点延迟高或网络抖动,整个 bulkWrite 就卡住直到超时(默认 30 秒):
- 日志、埋点类数据通常不需要强一致性,应降级为
{ w: 1 };订单类关键数据才用{ w: "majority" } -
w: 0不推荐:连主节点都不等确认,网络丢包或进程崩溃就真丢数据 - 务必加
timeoutMS: 30000,防止挂死;连接池也要配足:?maxPoolSize=20(默认 10,高并发时不够)
真正容易被忽略的是:批量插入后返回的 insertedIds 是 ObjectId 数组,但如果你依赖 _id 做后续关联,得确认这批写入是否真成功——ordered: false 下,部分失败不会中断流程,也不会报错,只藏在 writeErrors 里。










