必须用AsyncIOMotorClient而非PyMongo.MongoClient,因后者会阻塞事件循环;客户端须全局单例并在应用启停时通过on_event管理生命周期;数据库对象不可预固化collection引用,应按需获取或通过参数化Depends动态选择;聚合游标须显式to_list或async for消费;写操作需主动检查result.acknowledged;连接字符串末尾必须加?retryWrites=true。

必须用 AsyncIOMotorClient,不能用 PyMongo.MongoClient —— 后者哪怕套 async/await 也会阻塞事件循环,FastAPI 的并发优势直接归零。
连接实例必须全局单例,且生命周期绑定应用启停
Motor 客户端本身是线程安全、协程安全的,但频繁新建 AsyncIOMotorClient 实例会导致连接池失控、内存泄漏、DNS 频繁解析等问题。
- ✅ 正确做法:在
@app.on_event("startup")中创建 client,在@app.on_event("shutdown")中调用client.close() - ❌ 错误写法:
Depends函数里每次请求都AsyncIOMotorClient(...);或在模块顶层直接client = AsyncIOMotorClient(...)(此时无法 await 初始化) - ⚠️ 注意:不要在 startup 里
await client.admin.command("ping")—— 必须包在异步函数内,否则报RuntimeError: no running event loop
数据库对象不能提前固化 collection 引用
像 users_col = db["users"] 这种赋值一旦写成全局变量,多租户、多数据库场景下极易串库。Motor 的 collection 对象本身不保存状态,但引用会固定到某个 db 实例。
- ✅ 推荐模式:每个路由 handler 或依赖函数中按需获取
db["collection_name"],或封装为带参数的Depends(如def get_collection(name: str)) - ✅ 更安全的抽象:用
Depends返回AsyncIOMotorDatabase,让业务层自己选 collection,避免预绑定 - ⚠️ 常见坑:把
db["logs"]赋给全局变量后,在不同请求中混用,导致日志写进错误库或权限越界
聚合查询必须显式消费游标,不能直接 for 循环
collection.aggregate(pipeline) 返回的是 AsyncIOMotorCommandCursor,不是 list,也不是同步可迭代对象。直接 for doc in cursor: 会报 TypeError 或静默失败。
- ✅ 拿全部结果:用
await cursor.to_list(length=None)(小数据量)或await cursor.to_list(length=1000)(防 OOM) - ✅ 流式处理大数据:用
async for doc in cursor:,配合yield做 Server-Sent Events 或分块响应 - ⚠️ 性能陷阱:管道里含
$lookup却没在被关联集合上建索引,Motor 不会预警,查询延迟飙升且拖慢整个事件循环 - ⚠️ 连接池影响:聚合耗时长 +
maxPoolSize过小 → 其他请求卡在等待连接,建议高负载场景设为200~500
写操作默认不抛异常,必须主动检查结果
insert_one()、update_many() 等方法返回的是 Awaitable,但 MongoDB 写失败(如唯一键冲突、类型校验失败)不会自动 raise 异常,除非你显式 await 并检查 result.acknowledged 或 result.upserted_id 等字段。
- ✅ 安全写法:
result = await collection.insert_one({...}),然后判断if not result.acknowledged:或捕获OperationFailure - ✅ 开启写关注:
await collection.insert_one({...}, session=session)(事务内)或加write_concern=WriteConcern(w=1) - ⚠️ 默认行为风险:前端以为提交成功,实际数据没落库,且无日志、无告警
最易被忽略的一点:连接字符串末尾要加 /?retryWrites=true(MongoDB 4.0+ 默认启用),漏掉会导致某些重试逻辑失效,尤其在副本集切换时出现短暂写失败。这不是可选项,是生产环境强制要求。



















