SQLAlchemy 第二次取 aiomysql 连接,报 ping 缺少 reconnect 参数
SQLAlchemy 首次查询成功、第二次取连接报 ping 缺参,核对 PyMySQL 1.2.0 与旧异步方言组合,使用修复版本复验连接池。
第一次 engine.connect() 能执行查询,连接归还后再取一次,却在发出业务 SQL 之前失败:
TypeError: AsyncAdapt_aiomysql_connection.ping() missing 1 required positional argument: 'reconnect'
如果开启了 pool_pre_ping=True,并且堆栈经过 SQLAlchemy 的 MySQL 方言 do_ping(),先核对 SQLAlchemy 与 PyMySQL 的组合。这不等于 MySQL 密码错误,也不应靠增加连接池容量处理。
这一兼容问题明确涉及 SQLAlchemy 2.0.48、2.0.49 与 PyMySQL 1.2.0,aiomysql 0.2.0 和 0.3.2 都有相关记录;asyncmy 方言也受过影响。SQLAlchemy 2.0.50 的正式变更包含修复。不要被某些版本输出中的 2.2.8 误导,定位时以发行包版本为准。
为什么第一条查询正常
新建连接第一次交出时,一般无需对它预先 ping;连接回到池中后,后续取用才会进入预检查。因此连续两次“取出、使用、归还”,比在同一个连接里执行两条 SQL 更能暴露这个问题。
旧方言会根据 PyMySQL 的 ping() 签名判断是否需要显式传 False。PyMySQL 1.2.0 改变了相关默认行为,旧适配代码随后可能无参数调用异步适配器,而适配器仍要求 reconnect 参数。修复还要分别考虑 aiomysql 和 asyncmy 自身的连接签名,不能只给一个函数补默认值就认为所有组合都处理好了。
在发生错误的服务环境里读取发行信息:
python -m pip show SQLAlchemy aiomysql asyncmy PyMySQL
python -m pip check
未安装的驱动可忽略。实际应用使用哪个方言,由连接 URL 中的 mysql+aiomysql 或 mysql+asyncmy 等前缀决定,不是由“环境里装了哪几个包”决定。
使用包含修复的 SQLAlchemy,再复验两次取连接
按项目兼容范围选择包含修复的正式版本;2.0 分支可核对到的修复版本是 2.0.50,更高版本不必为此退回。保存依赖锁文件差异,让包管理器解析匹配的驱动组合,不手改适配器私有实现。
下面的诊断示例需要一个已授权、可连接的测试 MySQL 数据库。DATABASE_URL 由环境变量提供,避免把密码写入脚本或日志。查询只读取常量:
import asyncio
import os
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine
async def check_pool():
engine = create_async_engine(
os.environ["DATABASE_URL"],
pool_pre_ping=True,
pool_size=1,
max_overflow=0,
)
try:
for number in (1, 2):
async with engine.connect() as conn:
value = await conn.scalar(text("SELECT 1"))
print(number, value)
finally:
await engine.dispose()
if __name__ == "__main__":
asyncio.run(check_pool())
测试 URL 的前缀要与业务方言一致。这里让两次取用共享同一个小池、在同一个事件循环中完成,且每轮都退出连接上下文。预期两轮都得到 1,第二轮不再出现缺参错误;它不验证数据库重启或事务中途断线时的恢复。
关闭预检查只能改变触发条件
临时设置 pool_pre_ping=False 可能绕过这次 ping,但会同时放弃取连接时的存活检查,旧连接之后可能在业务查询中失败。若业务需要应对长时间空闲、网络切换或数据库重启,应优先修复依赖组合并保留预检查。
预检查本身也不能恢复已经执行到一半的事务。修复后仍有连接异常时,要把缺参 TypeError、真实断线和事务重试分开记录,避免将所有失败都归到同一个版本兼容问题。


