aiomysql.create_pool()必须用await或async with调用,否则返回协程对象而非连接池;连接和游标须每次查询重新获取且不可跨协程复用;需合理设置minsize、maxsize等参数并确保异常时归还连接。

aiomysql.create_pool() 必须用 async with 或 await 显式等待
直接调用 aiomysql.create_pool() 返回的是一个协程对象,不是连接池实例。很多人写成 pool = aiomysql.create_pool(...),结果后续调用 pool.acquire() 报 AttributeError: 'coroutine' object has no attribute 'acquire'。
正确做法只有两种:
- 在 async 函数里用
await aiomysql.create_pool(...) - 或用
async with aiomysql.create_pool(...) as pool:(推荐,自动关闭)
别试图用 loop.run_until_complete() 混合同步异步逻辑——这会破坏事件循环一致性,尤其在 FastAPI/Starlette 里直接崩溃。
执行 SQL 时必须用 await pool.acquire() + conn.cursor() 配合
aiomysql 的连接和游标都不是线程安全的,也不能跨协程复用。常见错误是把 conn 存成全局变量或类属性,然后多个请求并发读写,导致数据错乱或 OperationalError: MySQL Connection not available。
立即学习“Python免费学习笔记(深入)”;
每次查询都得走完整流程:
- 用
async with pool.acquire() as conn:获取连接(自动归还) - 再用
async with conn.cursor() as cur:获取游标(自动关闭) -
await cur.execute(...)执行语句,await cur.fetchall()或await cur.fetchone()取结果
增删改也一样,await cur.execute("INSERT ...", (val1, val2)) 后记得 await conn.commit();如果没开 autocommit,不 commit 就不会落库。
连接池参数不设 timeout 和 minsize 就等于没用
默认 minsize=1、maxsize=10、connect_timeout=10,看着够用,但实际高并发下容易卡死:
-
minsize太小(比如 0 或 1),冷启动时第一个请求要等建连,延迟突增 -
maxsize太大(比如 100),MySQL 侧可能被大量空闲连接拖垮(max_connections 被占满) -
connect_timeout和echo=False必须关掉,否则日志刷屏且影响性能
生产建议:minsize=5、maxsize=20、connect_timeout=5、pool_recycle=3600(防 MySQL wait_timeout 断连)。
事务嵌套或未捕获异常会导致连接卡住不归还
aiomysql 连接池不会自动回收「出错但没释放」的连接。典型场景:你在 async with pool.acquire() as conn: 块里执行 SQL 抛了未处理异常,conn 就永远滞留在池中,直到超时或服务重启。
务必做到:
- 所有数据库操作包在 try/except 里,确保
conn.rollback()或conn.commit()总被执行 - 避免在
async with块里 raise 未捕获异常(尤其是业务校验失败直接 raise) - 用
pool.close()+await pool.wait_closed()在应用退出前清理(如 FastAPI 的 lifespan)
连接池满了却查不到活跃连接?大概率是这里漏了异常处理,用 pool.size 和 pool.freesize 实时打印就能验证。
异步连 MySQL 不难,难的是每一步都得守住协程边界、连接生命周期和异常出口——少一个 await,多一个裸 raise,就可能让整个服务悄悄变慢。


















