asyncpg正确url为postgresql+asyncpg://,psycopg2不支持真异步;sqlalchemy 2.0异步需用create_async_engine配nullpool、expire_on_commit=false,并在fastapi依赖中用async with管理asyncsession生命周期。

asyncpg 与 psycopg 连接 URL 怎么写?
SQLAlchemy 2.0 的异步支持只通过 asyncpg(PostgreSQL)和 aiomysql(MySQL)等真正异步驱动实现,psycopg2 不行——它本质是同步阻塞驱动,哪怕加了 asyncio 包装层,连接池也会在 I/O 时挂起整个 event loop。
PostgreSQL 正确的异步连接 URL 是:
postgresql+asyncpg://user:pass@localhost:5432/dbname不是
postgresql://,也不是 postgresql+psycopg2://。少一个 +asyncpg,create_async_engine 会静默降级为同步模式(但不报错),后续调用 await session.execute(...) 会抛出 InvalidRequestError: Expected compiled object 或直接卡死。
create_async_engine 的关键参数有哪些?
异步引擎不能复用同步引擎那套 pool_size 和 max_overflow 逻辑。异步连接池由驱动自身管理,SQLAlchemy 只做协调:
-
poolclass必须设为NullPool:异步场景下 SQLAlchemy 自带的队列式连接池(QueuePool)不兼容 await/async,强行启用会导致连接泄漏或死锁 -
pool_pre_ping要关掉:预检依赖同步 I/O,开启后每次获取连接都会阻塞 event loop -
echo可开,但仅用于调试;生产环境开启会影响协程调度性能
推荐初始化方式:
from sqlalchemy.ext.asyncio import create_async_engine from sqlalchemy.pool import NullPool <p>engine = create_async_engine( "postgresql+asyncpg://user:pass@localhost:5432/db", poolclass=NullPool, echo=False, future=True )</p>
sessionmaker 的 expire_on_commit 为什么必须设为 False?
SQLAlchemy 2.0 异步 session 默认仍继承同步行为:执行 commit() 后自动过期所有已加载对象的属性。但异步 session 的 expire_on_commit=True 会触发隐式刷新(refresh),而 refresh 是同步操作,在 async context 中会报:
AttributeError: 'AsyncSession' object has no attribute '_proxies'或更隐蔽地导致后续
await session.get() 返回 stale 数据。
务必显式关闭:
from sqlalchemy.ext.asyncio import AsyncSession <p>AsyncSessionLocal = sessionmaker( bind=engine, class_=AsyncSession, expire_on_commit=False # 关键! )</p>
如何安全地在 FastAPI 依赖中提供 AsyncSession?
别直接在依赖里 yield AsyncSessionLocal()——没 await 关闭,连接不会归还池中,几次请求后就耗尽连接:
- 必须用
async with管理生命周期 - 依赖函数本身得是
async def - FastAPI 的
Depends支持 async 依赖,但中间不能夹同步代码
正确写法:
async def get_db():
async with AsyncSessionLocal() as session:
yield session # 注意:这里 yield 的是已 enter 的 session
如果漏掉 async with,或写成 return AsyncSessionLocal(),连接对象会在函数返回后被 GC,但底层 socket 可能未关闭,asyncpg 会报 Too many connections 或 connection is closed。
实际用的时候,最易忽略的是 NullPool 和 expire_on_commit=False 这两个开关——它们不出错,但会让异步变成“伪异步”,压测时响应时间曲线和同步几乎一样。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











