bulk_write是性能分水岭,10万条数据可从200秒降至3–8秒;支持混合操作、per-operation write_concern、ordered=false并发执行,但需检查writeerrors,批次大小应据bson体积动态调整。

直接用 bulk_write,别用 insert_many 或循环 insert_one——这不是优化建议,是性能分水岭。10 万条数据,单条写要 200 秒以上,bulk_write 分批后通常压到 3–8 秒。
为什么 bulk_write 比 insert_many 更值得细调
insert_many 是 bulk_write 的简化封装,它默认 ordered=True,且不支持混合操作;而 bulk_write 可在同一请求里混用 InsertOne、UpdateOne、DeleteOne,还能为每条操作单独设 upsert 和 write_concern。
- 想给一批用户补字段,另一批用户删记录?
insert_many做不到,必须用bulk_write - 部分数据要等主从同步(
w: "majority"),部分只要主节点落盘(w: 1)?只有bulk_write支持 per-operation 级别的write_concern - 误设
ordered=True后某条文档因唯一键冲突失败,整批后续全跳过——这种静默丢数据,在日志类场景里极难排查
ordered=False 不是开箱即用的加速器,得配错误检查
设 ordered=False 后,MongoDB 会并发执行所有操作,失败项不影响其余项,但错误不会抛异常,而是塞进返回值的 writeErrors 字段里。不主动读这个字段,就等于把失败当成功。
- 必须检查
result.writeErrors,尤其在 ETL 或埋点写入中,漏掉几百条失败文档可能几周都发现不了 - 在分片集群中,
ordered=False才能让mongos并行投递到多个分片;ordered=True是串行路由,天然卡瓶颈 - PyMongo 默认是
ordered=True,不显式传参就不是“无序”
批次大小别硬套“1000”,先看单条 BSON 体积
批次大小不是越大越好,关键约束是单个请求体不能超 MongoDB 的 BSON 16MB 上限。如果单条文档平均 5KB,那 3000 条就接近 15MB,再加点元数据就超限;若文档含大字段(如 base64 图片),300 条就可能爆。
- 实测安全区间是 100–500 条/批:小于此数,网络节省不明显;大于此数,容易触发
DocumentTooLarge或服务端 OOM - 不要一次性构造全部
requests列表再传给bulk_write,边查边攒、攒够一批就发一批,避免客户端内存暴涨 - 对更新类操作,优先用
UpdateMany替代多个UpdateOne;后者每条都要独立filter,解析开销翻倍
write_concern 必须按数据等级显式设,别信“默认”
副本集里写入慢,80% 是因为 write_concern 卡在等——比如设了 w: "majority",但某个从节点延迟高或网络抖动,整个 bulk_write 就卡住直到超时(默认 30 秒)。
- 日志类、埋点类数据通常不需要强一致性,应降级为
w: 1;订单类关键数据才用w: "majority" -
w: 0不推荐:连主节点都不等确认,网络丢包或进程崩溃就真丢数据 - 如果用了
w: "majority",必须确保所有从节点oplogSize足够大(至少容纳 24 小时变更),否则从节点追不上主节点,写入会持续阻塞
真正卡住批量写入的,往往不是代码有没有用 bulk_write,而是批次是否按 BSON 体积动态拆分、write_concern 是否按业务分级、writeErrors 是否被当成空列表忽略——这些细节不处理,再快的接口也跑不出预期吞吐。











