motor查询返回asynciomotorcursor游标而非数据,必须await其to_list()、next()等方法或使用async for遍历,不可直接await find()或同步for循环,否则报错;聚合操作同理,且需注意内存控制与连接池复用。

Motor 本身不支持直接 await 查询结果,必须用 await 调用游标方法(如 to_list() 或 next()),否则会得到一个游标对象而非数据。
Motor 查询返回的是游标,不是数据
很多人写 await collection.find({}) 后直接尝试遍历或取字段,结果报错 TypeError: 'AsyncIOMotorCursor' object is not iterable。Motor 的 find() 返回的是 AsyncIOMotorCursor,它本身不可 await,也不可直接 for 循环。
- 正确做法是调用游标上的异步方法:比如
await cursor.to_list(length=100)拉取全部,或await cursor.next()取单条 - 如果只查一条,优先用
await collection.find_one({})—— 它直接返回 dict 或 None,无需额外 await 游标 -
to_list()的length参数设为None表示不限数量,但生产环境慎用,容易 OOM
聚合管道(aggregate())要显式 await 游标
aggregate() 和 find() 一样返回 AsyncIOMotorCommandCursor,不能直接 await,也不能直接 list()。
- 常见错误:
list(await collection.aggregate([...]))——list()是同步操作,无法消费异步游标 - 正确写法:
result = await collection.aggregate([...]).to_list(100) - 如果聚合结果可能很大,改用
async for doc in collection.aggregate([...]):流式处理,避免内存堆积 - 注意:
to_list()会一次性把所有结果加载进内存;async for更省内存,但需确保事件循环不被阻塞
连接池与生命周期管理容易被忽略
Motor 客户端(AsyncIOMotorClient)是线程安全的,但**不是进程安全的**,且内部维护连接池 —— 它应该被复用,而不是每次查询都新建。
- 错误做法:在每个请求 handler 里 new 一个
AsyncIOMotorClient,会导致连接泄漏和性能下降 - 推荐方式:在应用启动时创建单例 client,并通过依赖注入或全局变量共享(FastAPI 中可用
lifespan,Tornado 中可在 app 初始化时挂载) - 关闭连接不是必须的,但若需优雅退出(如测试 teardown),应调用
await client.close(),否则可能残留连接 - 默认连接池大小是 100,可通过
maxPoolSize参数调整,高并发场景下注意是否够用
Motor 的“异步感”很弱,表面看只是加了 await,但游标、连接复用、内存控制这些点一旦漏掉,就容易在压测或长时间运行后暴露问题。











