You need to enable JavaScript to run this app. FastAPI下aiomysql连接池达maxsize后卡死 疑async with未自动释放连接
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI下aiomysql连接池达maxsize后卡死 疑async with未自动释放连接

aiomysql连接池到maxsize就卡死的根因

你观察到的"调用N次(N等于maxsize配置值)后连接池卡死"的现象,本质是连接归还逻辑完全失效,池内连接名额被一次性占满后无可用连接分配,后续请求会永久等待获取连接。

核心错误点拆解

  • 你对aiomysql连接池的异步上下文使用逻辑完全写反了,这是最根本的问题。
    aiomysql的pool.acquire()本身就是为异步上下文设计的入口:正常流程下,async with语法会自动await这个协程,从池内取出一个可用连接,并且在退出上下文时,自动触发连接池的归还流程,把连接重新标记为可用放回池队列,同时把已占用的名额计数减一。
  • 你当前的写法async with await pool.acquire() as conn,执行流程完全偏离设计预期:
    1. 第一步先执行await pool.acquire():此时连接池已经从内部可用队列取出一个连接,将池内的"已占用连接计数"+1,把连接对象返回给你。
    2. 第二步你把这个返回的连接对象当成异步上下文管理器套了一层async with:退出上下文时,触发的是连接对象本身的__aexit__方法,这个方法的职责是直接物理关闭这个数据库的TCP连接,完全不会通知连接池"这个连接已经用完了,请更新计数把它放回可用队列"。
  • 长期执行的结果刚好匹配你的测试现象:每一次请求,都会实打实消耗一个连接池的已占用名额,哪怕实际的物理连接被关掉了,连接池也永远不会回收这些名额。因为前N次请求虽然没归还连接,但每次都成功建立了新的物理连接完成查询,表面上看起来一切正常,直到已占用计数达到你配置的maxsize阈值,池内再也拿不出可用连接,后续所有调用acquire的请求都会无限等待,表现为程序卡死。

你的认知误区

你原本以为async with会自动帮你把连接还给池,实际上你当前写的async with是套在连接对象上的,它的作用是关闭物理连接,根本不是连接池层面的归还逻辑。真正负责归还连接、释放池名额的上下文管理器,是连接池自己提供的pool.acquire()入口,而非连接对象本身。
这个提前await协程再套上下文的写法,是aiomysql使用者最容易踩的坑,很多人因为习惯了普通同步上下文的写法,无意中触发了这个连接不归还的问题。

修正方案

只需要把获取连接的写法调整为正确的形式,去掉提前await的步骤即可:

async def _get_query_with_pool(pool):
    # 正确写法:直接将pool.acquire()放到async with后,不要提前await
    async with pool.acquire() as conn:
        async with conn.cursor(aiomysql.DictCursor) as cur:
            await cur.execute("SELECT 1")
            return await cur.fetchall()

额外优化建议

  • 可以在创建连接池的时候加上pool_recycle=3600参数,自动回收长时间空闲的连接,避免数据库端主动断开空闲会话导致池内出现死连接。
  • 不要在拿到池内连接后手动调用conn.close(),池的核心价值就是复用物理连接,手动关闭会直接破坏连接池的复用逻辑。

内容的提问来源于stack exchange,提问作者luthfianto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.03 14:45:05