应优先使用 insert_many() 替代循环 insert_one(),因其打包发送、自动分块、支持无序插入;注意 $ 键校验差异、预设 _id、w=0 提升吞吐但有丢失风险、批量大小宜 100–500 条、复用 collection 实例。

用 insert_many() 替代循环 insert_one()
单条写入在批量场景下会触发大量网络往返,每个 insert_one() 都是一次独立的 RPC 请求。MongoDB 官方驱动明确建议:只要数据量超过几条,就该用 insert_many()。
它把多条文档打包成一个请求发往服务端,显著降低 TCP 握手、序列化、网络延迟叠加带来的开销。
- 默认会自动分块(每 1000 条为一批),避免单次请求超限;可通过
ordered=False启用无序插入,失败单条不中断其余写入 - 注意:如果文档里含
$开头的键(如{"$id": "xxx"}),insert_many()会直接报InvalidDocument,而insert_one()可能静默忽略——这是容易漏掉的校验差异 - 插入前建议统一调用
ObjectId()或生成_id字段,否则服务端逐条生成会增加 CPU 压力
提前禁用写关注(w=0)并确认是否可接受丢失
默认写操作使用 w=1(等待主节点确认),对吞吐影响极大。若业务允许短暂丢失(如日志、埋点、缓存预热),设为 w=0 能让写入变成“发完即返”,实测吞吐可提升 3–5 倍。
配置方式有两种:
- 连接时全局设置:
MongoClient(..., w=0) - 单次操作覆盖:
collection.insert_many(docs, write_concern=WriteConcern(w=0))
⚠️ 注意:w=0 下网络丢包或服务端崩溃会导致数据静默丢失,且无法通过 getLastError 检测——不是所有“能快”的场景都适合它。
控制批量大小:100–500 条/批比盲目堆大更稳
很多人以为“越大越好”,但实际中单批 5000 条常触发 DocumentTooLarge 或 OOM。MongoDB 单次请求上限默认 16MB,还要预留 BSON 序列化开销和网络缓冲区。
推荐按文档平均大小反推:
- 平均文档 ≤ 2KB → 每批 200–300 条较稳妥
- 含大字段(如 base64 图片)→ 强烈建议压到 50 条以内,并启用
bypass_document_validation=True减少校验耗时 - 配合
max_time_ms设置超时(如insert_many(..., max_time_ms=5000)),避免某批卡死拖垮整个流程
避免在循环内重建 Collection 实例
常见错误是每次写入都重新获取 db.collection,看似无害,实则触发连接池查找+命令路由计算,累积开销可观。
正确做法是复用同一个 Collection 对象:
# ❌ 错误:每次都在循环里取
for chunk in chunks:
db.my_collection.insert_many(chunk) # 这里隐式调用 __getattr__
<h1>✅ 正确:提前提取</h1><p>col = db.my_collection
for chunk in chunks:
col.insert_many(chunk)
</p>
尤其在高并发写入时,这个微小差别会让整体耗时波动明显——它不报错,但慢得没理由。
真正麻烦的是混合了事务、session 或自定义 codec 的场景,这时 Collection 复用还涉及状态隔离问题,得具体看是否启用了 start_session()。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











