Motor不是PyMongo的异步封装,而是基于asyncio重写的异步驱动;PyMongo所有I/O操作同步阻塞,不可await,否则报TypeError;Motor需用await调用、async for遍历游标,并显式管理客户端生命周期。

Motor 是 PyMongo 的异步封装,不能直接 await PyMongo 的方法,必须用 Motor 提供的对应类和方法。
为什么不能直接在 asyncio 里用 PyMongo
PyMongo 所有 I/O 操作(如 find_one、insert_one)都是同步阻塞的,放进 async def 函数里 await 会报 TypeError: object dict can't be used in 'await' expression。Motor 重写了整个客户端栈,底层用 asyncio + libev 或 asyncio.get_event_loop().run_in_executor(取决于版本)做非阻塞调度。
常见错误现象:
- 误写
await collection.find_one(...)(PyMongo 返回 dict,不可 await) - 混用
motor.motor_asyncio.AsyncIOMotorClient和pymongo.MongoClient实例 - 忘记在连接字符串中显式加
appname或maxPoolSize,导致高并发下连接耗尽
初始化 AsyncIOMotorClient 的关键参数
Motor 客户端不是线程安全的,也不该被多个协程共享未加锁的实例——但可以安全地复用同一个 AsyncIOMotorClient 实例(它内部管理连接池)。重点参数:
立即学习“Python免费学习笔记(深入)”;
-
host和port可以合并为host="mongodb://localhost:27017",推荐用 URI 形式 -
maxPoolSize=100:默认是 100,但若 QPS 超过 500,建议调到 200+;注意 MongoDB 服务端也有maxConns限制 -
minPoolSize=5:避免冷启动时每次新建连接,但设太高会提前占满服务端连接数 -
serverSelectionTimeoutMS=3000:超时设太短会导致偶发ServerSelectionTimeoutError,尤其在副本集切换期间 -
connect=False:设为True会在实例化时立即尝试连接(同步阻塞),应保持默认False,改用await client.admin.command("ping")显式探测
常用操作的写法与陷阱
所有集合级操作返回的是 coroutine 对象,必须 await;游标(AsyncIOMotorCursor)也需 await 才能取值。典型写法:
client = AsyncIOMotorClient("mongodb://localhost:27017")
db = client["mydb"]
collection = db["users"]
<h1>✅ 正确:await 每个异步操作</h1><p>user = await collection.find_one({"_id": ObjectId("...")})
await collection.insert_one({"name": "Alice", "ts": datetime.now()})</p><h1>❌ 错误:没 await,user 是 coroutine 对象,后续 .get() 会报 AttributeError</h1><p>user = collection.find_one({"_id": ...}) # 忘了 await</p><h1>✅ 游标要 await list() 或用 async for</h1><p>async for doc in collection.find({"status": "active"}):
print(doc["name"])</p><h1>⚠️ 注意:collection.find() 本身不触发网络请求,只有遍历或 await list() 才真正执行</h1><p>性能影响点:
- 频繁
await collection.find().to_list(length=1000)会一次性加载全部文档进内存,大数据量时易 OOM;优先用async for -
update_many默认不返回匹配数,要加upsert=False, bypass_document_validation=False等显式控制行为 - 事务必须在 session 内执行:
async with await client.start_session() as s: await s.start_transaction(),漏掉start_transaction会导致写操作不被隔离
连接泄漏与生命周期管理
Motor 客户端没有自动关闭机制,client.close() 是异步方法,必须显式 await;否则进程退出前未 close 会导致连接句柄泄漏,Docker 环境下表现为 Connection refused 或 Too many open files。
推荐模式:
- 全局单例 client(模块级变量),应用启动时创建,退出前
await client.close() - Web 框架(如 FastAPI)中用 lifespan event:
@app.on_event("shutdown")中 await close - 测试时每次用
pytest-asynciofixture 创建新 client,并确保 teardown 中 await close
容易被忽略的是:即使只读操作,也要等 await client.close() 完成后再让主程序 exit,否则部分连接可能卡在 FIN_WAIT2 状态,持续占用端口。



















