asyncpg性能最强因直连二进制协议,生产必须用create_pool()连接池而非connect();min_size设为平均并发1.2–1.5倍,max_size不超数据库max_connections的70%,需设max_inactive_time=300秒防泄漏。

asyncpg 是目前 Python 生态中性能最强的异步 PostgreSQL 驱动,它不走 DB-API 兼容层、不依赖 psycopg2,而是直接实现 PostgreSQL 二进制协议,因此在高并发读写场景下优势明显。如果你的应用正面临连接阻塞、QPS 上不去、或 psycopg2 在 asyncio 环境中被迫用线程池兜底的窘境,换用 asyncpg 并合理调优,通常能带来 3–10 倍的吞吐提升。
为什么不能直接用 asyncpg.connect() 处理并发请求
单次 asyncpg.connect() 创建的是独占连接,无法复用;在 Web 请求中每来一个请求就新建连接,很快会耗尽数据库连接数(PostgreSQL 默认 max_connections=100),并触发 asyncpg.exceptions.TooManyConnectionsError。更严重的是,TCP 握手 + SSL + 认证开销会让首字节延迟飙升。
正确做法是全程使用连接池:
-
asyncpg.create_pool()是唯一推荐的生产级入口,它内部管理连接生命周期、自动重连、支持连接预热 -
min_size建议设为应用平均并发请求数的 1.2–1.5 倍(例如常驻 8 个活跃连接,设min_size=10) -
max_size不宜盲目拉高——超过数据库max_connections的 70% 就可能引发拒绝连接;建议先压测再定(常见值:15–30) - 务必设置
max_inactive_time=300(单位秒),否则空闲连接长期不释放,会卡住连接池回收逻辑
executemany() 批量插入时为何有时比循环 execute() 还慢
这不是 asyncpg 的 bug,而是误用了批量接口的典型表现。当传入的 data 是生成器(generator)、或元素数量极少(len(data) < 5)、或字段类型混杂(比如混合了 None 和 datetime)时,executemany() 内部的参数序列化反而增加 CPU 开销。
立即学习“Python免费学习笔记(深入)”;
实战建议:
- 确保
data是 list 或 tuple,且长度 ≥ 10;小于该阈值直接用execute()+VALUES ($1,$2),($3,$4)拼接更稳 - 避免在
executemany()中混用json和bytea字段——不同编解码路径会触发多次缓存 miss - 若需动态列名(如 ETL 场景),不要强行塞进
executemany(),改用事务包裹多个execute()并显式await conn.executemany(...)
事务里用 fetch() 后再 execute() 报 asyncpg.exceptions.InFailedSQLTransaction
这是 PostgreSQL 协议层的硬性限制:一旦事务中某条语句出错(哪怕只是 WHERE id = -1 查不到数据),整个事务就进入“failed”状态,后续任何非 ROLLBACK 操作都会被拒绝。
常见踩坑点:
-
conn.fetchrow("SELECT ...")返回None本身不是错误,但若紧接着执行conn.execute("UPDATE ...")而没做if row is not None:判断,逻辑上可能漏掉分支,但不会报这个错 - 真正触发该异常的是类似
conn.execute("INSERT INTO t(pk) VALUES (1)")因主键冲突抛出UniqueViolationError后,还继续发其他execute() - 解决方式只有两个:显式捕获异常后
await tx.rollback(),或改用SAVEPOINT隔离子操作(await tx.execute("SAVEPOINT sp1")→ 出错时await tx.execute("ROLLBACK TO sp1"))
缓存语句(statement_cache_size)设多大才合适
asyncpg 默认开启语句缓存(statement_cache_size=100),它把已解析的 SQL 文本 + 类型映射存内存里,避免重复解析。但缓存不是越大越好。
关键判断依据:
- 如果应用中 90% 的查询来自固定模板(如
"SELECT * FROM users WHERE status = $1"),保持默认 100 完全够用 - 若大量使用动态 SQL(比如拼接
ORDER BY字段、不定长IN ($1, $2, ..., $N)),缓存命中率会暴跌,此时设为0反而降低内存抖动 - 注意:缓存大小只影响客户端,不影响 PostgreSQL 服务端的 prepared statement;
asyncpg不自动使用服务端预编译,除非你手动调用conn.prepare()
实际部署时最容易被忽略的是连接池和事务的生命周期绑定关系:连接池里的每个连接都维护独立的事务状态,acquire() 得到的连接若未在作用域内完成 commit() 或 rollback(),该连接会被标记为“不可复用”,直到超时后强制关闭。这意味着——事务必须在 async with pool.acquire() as conn: 块内结束,不能跨 await 拆开。



















