Python操作MongoDB并发写入性能问题主因是未配置连接池和滥用insert_one();应设maxPoolSize=50、minPoolSize=10等参数,并改用分片的insert_many()批量插入。

并发写入 MongoDB 时直接用 insert_one() 循环调用,90% 的性能问题都出在这儿——不是 MongoDB 慢,是 Python 客户端没用对连接池和批量接口。
为什么并发插入会变慢甚至失败
常见现象包括:CPU 占用飙升、ConnectionResetError 频发、响应延迟从几十毫秒跳到数秒、部分文档写入丢失。根本原因不是 MongoDB 本身扛不住,而是 PyMongo 默认行为在高并发下暴露了三个硬伤:
- 未显式配置连接池,导致每次请求都尝试新建 TCP 连接(哪怕复用同一个
MongoClient实例) - 用
insert_one()逐条提交,每条都触发一次网络往返 + 服务端解析 + WAL 写入 - 没处理写入异常的粒度——单条失败会导致整批中断,或静默丢弃
必须设置的 MongoClient 连接池参数
不改这些参数,再多线程/协程也白搭。以下为生产环境实测有效的最小集:
-
maxPoolSize=50:避免连接耗尽;低于 20 在 QPS > 100 时大概率出现ConnectionTimeout -
minPoolSize=10:预热空闲连接,消除冷启动抖动 -
socketTimeoutMS=5000:防止个别慢查询拖垮整个连接池 -
serverSelectionTimeoutMS=3000:副本集故障转移时快速失败,别卡住 - 务必关闭自动重试:
retryWrites=False(尤其在事务外),否则重复写入风险陡增
示例初始化代码:
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
from pymongo import MongoClient <p>client = MongoClient( "mongodb://localhost:27017/", maxPoolSize=50, minPoolSize=10, socketTimeoutMS=5000, serverSelectionTimeoutMS=3000, retryWrites=False ) db = client["myapp"] collection = db["events"]
批量写入必须用 insert_many(),且要分片
insert_many() 不是“语法糖”,它把 N 次请求压缩成 1 次 wire 协议消息,实测 1 万条数据写入从 8.2s 降到 1.1s。但要注意两个坑:
- 单次
insert_many()不宜超过 1000 条文档——WiredTiger 默认单次操作有 BSON 大小限制(16MB),超限抛DocumentTooLarge - 必须捕获
BulkWriteError并检查details["writeErrors"],不能只看是否抛异常;部分失败时其他文档仍会写入 - 若需强一致性(如计费类场景),应在调用前加
ordered=False,允许并行执行并收集全部错误
简单分片示例:
def bulk_insert_safe(collection, docs, chunk_size=500):
for i in range(0, len(docs), chunk_size):
chunk = docs[i:i + chunk_size]
try:
collection.insert_many(chunk, ordered=False)
except BulkWriteError as e:
# 处理 chunk 内部分失败
for error in e.details.get("writeErrors", []):
print(f"第 {error['index']} 条失败: {error['errmsg']}")
多进程/多线程下 MongoClient 是线程安全的,但别乱共享
MongoClient 实例本身支持多线程复用,但以下情况必须隔离:
- 使用
multiprocessing时:子进程 fork 后会继承父进程的 socket 句柄,但 MongoDB 连接状态不同步,极易出现InvalidSession——每个子进程应单独创建MongoClient - 异步框架(如 FastAPI + asyncio):不要在全局 scope 创建 client 后跨协程共用;推荐用 dependency injection 或
lifespan管理生命周期 - 不同数据库权限需求:比如一个服务既要写
logs又要读config,应拆成两个 client 实例,避免 auth context 冲突
最容易被忽略的一点:连接池大小是按 MongoClient 实例计的,不是按进程/线程。开 10 个进程各持一个 client 且 maxPoolSize=50,实际可能占用 500 个连接——务必结合 MongoDB 的 maxConns 配置做压测验证。

















