motor基于asyncio异步i/o模型,使用streamreader/streamwriter注册事件循环,支持协程安全连接池和显式配置,聚合需async for或to_list(),写操作须手动检查acknowledged,非pymongo简单替换。

Motor 的 I/O 模型天然适配 asyncio 事件循环
PyMongo 所有网络操作(find_one、insert_one、aggregate)都基于同步阻塞 socket,调用时会直接挂起当前线程,直到 MongoDB 返回响应;而 AsyncIOMotorClient 内部使用 asyncio.StreamReader/StreamWriter,所有读写都注册到事件循环,不占用线程资源。这意味着:同一协程栈里并发 1000 个查询,Motor 只需一个线程 + 事件循环调度,PyMongo 则需至少 1000 个 OS 线程(或大量线程池调度开销)。
连接池复用与生命周期可控
PyMongo 的 MongoClient 连接池是线程局部的,跨线程无法共享;在 run_in_executor 中每调一次就可能新建连接,容易触发 MaxPoolSize 限制或 ConnectionAbortedError。Motor 的 AsyncIOMotorClient 连接池是协程安全的,所有 await 操作共享同一组异步连接,且支持显式配置:
-
maxPoolSize=300(高并发场景下必须调大,默认 100 容易耗尽) -
minPoolSize=20(避免冷启动抖动) -
retryWrites=True(副本集/分片环境下不设会导致写失败静默丢弃)
ServerSelectionTimeoutError 或 TimeoutError: Connection pool is full 就不是偶然,而是必然。
聚合与游标必须用 async for 或 to_list(),不能“假装 await”
PyMongo 的 collection.aggregate(pipeline) 返回可迭代对象,能直接 for doc in cursor;Motor 的 aggregate() 返回 AsyncIOMotorCommandCursor,它既不是可迭代对象,也不能直接 await——常见错误是写 await collection.aggregate(pipeline),结果报 TypeError: object AsyncIOMotorCommandCursor can't be used in 'await' expression。正确方式只有两种:
async for doc in collection.aggregate(pipeline): ...-
results = await collection.aggregate(pipeline).to_list(length=None)(length=None表示取全部,别漏写,否则默认只取 101 条)
写操作默认不抛异常,必须手动检查结果
PyMongo 的 insert_one() 遇到唯一索引冲突会立即抛 DuplicateKeyError;Motor 的同名方法默认“发出去就返回”,即使 MongoDB 回复写入失败(如类型校验失败、磁盘满),也只返回 InsertOneResult 对象,result.acknowledged 为 False,但不会 raise 异常。如果你没检查 result.acknowledged 或 result.upserted_id,数据就静默丢了。这是和 PyMongo 最危险的差异,也是线上事故高频点。
collection.find() 直接换成 await collection.find() 会报错,因为返回值类型、生命周期管理、错误传播机制全都不兼容。真正落地时,最易被跳过的其实是客户端单例化和 client.close() 的 await 调用——不 await 关闭,连接可能 linger 在 ESTABLISHED 状态,数小时后才被内核回收。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











