必须使用全局单例 AsyncIOMotorClient,否则连接池失控、DNS重复解析、内存上涨、高并发触发 MaxPoolSize 限制或连接超时;应在应用启动时初始化并挂载到 app.state,关闭时显式释放,依赖中仅复用 client 而非新建。

必须用全局单例 AsyncIOMotorClient,不能在每次请求或依赖中新建实例——否则连接池会失控、DNS反复解析、内存持续上涨,高并发下直接触发 MaxPoolSize 限制或连接超时。
为什么不能在 Depends 里创建新 client
常见错误是把连接逻辑写进依赖函数,比如:
async def get_db():
client = AsyncIOMotorClient("mongodb://...") # ❌ 每次调用都新建
return client["mydb"]这会导致:
- 每次请求都新建一个
AsyncIOMotorClient实例,连接池无法复用 - DNS 解析重复触发(尤其在容器或云环境里延迟明显)
- 连接数线性增长,
maxPoolSize=100很快被耗尽,后续请求卡在ServerSelectionTimeoutError - 客户端未 close,Python 进程退出前可能残留 TCP 连接,影响部署滚动更新
正确做法:应用启停时管理 client 生命周期
把 AsyncIOMotorClient 实例挂到 app.state,只初始化一次:
from fastapi import FastAPI
from motor.motor_asyncio import AsyncIOMotorClient
<p>app = FastAPI()</p><p>@app.on_event("startup")
async def startup_db_client():
app.state.mongo_client = AsyncIOMotorClient(
"mongodb://admin:pass@localhost:27017/?retryWrites=true",
maxPoolSize=100,
minPoolSize=10
)</p><p>@app.on_event("shutdown")
async def shutdown_db_client():
app.state.mongo_client.close()然后在依赖中只取数据库对象,不碰 client:
async def get_db():
return app.state.mongo_client["myapp"] # ✅ 复用同一 client注意:retryWrites=true 必须加在连接字符串末尾,否则副本集写入可能失败;minPoolSize 设为非零值可避免冷启动时首次查询慢。
别提前固化 collection 引用
有人为了“方便”,在模块顶层写:
db = app.state.mongo_client["myapp"] users_col = db["users"] # ❌ 全局变量,多租户/多 DB 场景下极易串库
后果包括:
- 不同租户请求共用同一个
users_col,数据写错集合 - 测试时 mock db 失效(引用已固化,无法注入测试 client)
- 切换数据库名需改多处,易漏
推荐方式是按需获取:
async def get_user_collection(db: AsyncIOMotorDatabase = Depends(get_db)):
return db["users"] # ✅ 每次都从当前 db 实例取或者更灵活地参数化:
async def get_collection(name: str) -> AsyncIOMotorCollection:
return app.state.mongo_client["myapp"][name]聚合游标必须显式消费,否则连接不释放
collection.aggregate(pipeline) 返回的是 AsyncIOMotorCommandCursor,它本身不占用连接,但一旦你开始 async for 或调用 to_list(),底层连接就会被占用直到游标关闭。
常见坑:
- 写了
cursor = col.aggregate([...])就返回,没消费 → 游标悬空,连接不归还池 - 用
await cursor.to_list(length=None)加载大数据集 → OOM 或拖慢整个事件循环
安全做法:
# 小结果集(< 1k) docs = await cursor.to_list(length=1000) <h1>流式处理大结果</h1><p>async for doc in cursor: process(doc) # ✅ 自动释放连接
特别注意:如果聚合里用了 $lookup 或 $facet,游标生命周期更长,务必确保消费完成或加超时控制。


















