motor连接池耗尽主因是异步场景下maxpoolsize设置不合理、client.close()被忽略或事务未正确结束;其比pymongo更易耗尽,因协程并发高、会话独占连接且不自动回收、短生命周期服务常漏关连接。

Motor 连接池耗尽不是 Motor 本身的问题,而是 PyMongo 驱动层连接池配置与异步生命周期不匹配导致的——根本症结在 maxPoolSize 设置不合理、client.close() 被忽略,或事务未正确结束。
为什么 Motor 的连接池比同步 PyMongo 更容易耗尽?
Motor 是 PyMongo 的异步封装,底层复用完全相同的连接池逻辑。但异步场景下有三个放大效应:
- 协程轻量,应用可能无意中并发发起数百请求,远超同步线程数,而
maxPoolSize默认仍为 100,瞬间打满 -
await client.start_session()创建的会话若未显式session.endSession()(尤其在异常路径中),连接会被长期独占,且 Motor 不自动回收 - Serverless 或短生命周期服务(如 FastAPI 的 background task)中,常漏掉
await client.close(),导致连接残留、DNS 缓存未清、TCP 连接未 FIN
Motor 中 maxPoolSize 和 waitQueueTimeoutMS 怎么设才合理?
不能照搬同步项目的值。需结合协程并发峰值和平均事务时长反推:
- 先用
db.currentOp({secs_running: {$gt: 1}})查看 P95 事务耗时(比如 420ms) - 估算峰值 QPS(比如 300),则活跃连接 ≈ 300 × 0.42 ≈ 126;设
maxPoolSize=180(×1.5 缓冲) -
waitQueueTimeoutMS必须显式设为略大于 P99 耗时(如 6000ms),避免请求无限排队拖垮整个 event loop - URI 示例:
mongodb://host:27017/?maxPoolSize=180&waitQueueTimeoutMS=6000&minPoolSize=10
Motor 事务里最容易漏掉的 endSession() 怎么兜住?
Motor 没有 with_transaction() 自动清理机制(PyMongo 有,Motor 没有对应异步版本),必须手动保障:
- 永远用
try/finally包裹事务体,确保session.endSession()执行 - 不要依赖
async with—— Motor 的AsyncClientSession不是异步上下文管理器 - 示例写法:
session = await client.start_session() try: await session.start_transaction() await collection.insert_one({...}, session=session) await session.commit_transaction() finally: await session.endSession() - 若使用
fastapi.Depends注入 session,务必在 dependency 的finally或async_exit中调用endSession()
连接池耗尽时,如何快速定位是哪类操作在卡连接?
靠日志不够,得直接查 mongos 或 mongod 实时状态:
- 在 mongos 上运行:
db.currentOp({secs_running: {$gt: 2}, "secs_running": {$exists: true}}),重点关注secs_running高、且type: "idleSession"的条目——这些就是没endSession()的“僵尸会话” - 检查连接来源:
db.currentOp().inprog.forEach(op => print(op.client)),比对 IP + 端口是否集中来自某台应用服务器 - 用
netstat -an | grep :27017 | wc -l对比客户端声明的连接数与 mongos 实际维持数,若后者远高于前者 × shard 数,说明存在连接泄漏而非单纯池小
真正难处理的不是参数调大,而是异步代码里那些没被 await 的 session.endSession()、没进 finally 的异常分支、以及被 asyncio.shield() 保护后跳过 cleanup 的后台任务——这些地方一旦漏掉,连接就永远卡住。











