asyncpg连接池初始化慢但查询快,因预编译和缓存更激进;psycopg[async]初始化轻量但批量插入慢3–5倍;两者参数绑定、类型处理、批量操作机制差异显著,需按场景选型并规范使用。
![python asyncpg vs psycopg[async] 的性能实测](https://img.php.cn/upload/article/001/242/473/177148387119830.jpg)
asyncpg 的连接池初始化比 psycopg[async] 慢得多,但首次查询后反超
asyncpg 启动时要加载大量类型映射和协议解析逻辑,asyncpg.create_pool() 在高并发预热阶段可能比 psycopg.AsyncConnection.connect() 多花 2–3 倍时间。这不是 bug,是它为后续查询做更激进的预编译和缓存导致的。
实操建议:
- 别在每次请求里新建 pool——用全局单例或依赖注入生命周期管理
asyncpg.Pool - 如果服务启动后有冷启动抖动,加个
await pool.execute("SELECT 1")预热 - psycopg[async] 的
AsyncConnection初始化轻量,适合短命任务(比如 CLI 脚本),但连接复用率低时反而更慢
psycopg[async] 对 SQL 注入更“宽容”,asyncpg 严格校验参数绑定
asyncpg 要求所有参数必须显式通过 $1, $2 占位符传入,不支持字典键名绑定;psycopg[async] 允许 %(name)s 和 %s 混用,甚至容忍部分字符串拼接(危险!)。
常见错误现象:
立即学习“Python免费学习笔记(深入)”;
-
asyncpg.exceptions.IndeterminateStatementError:用了未声明的变量名或混合了位置/命名绑定 -
psycopg.errors.ProgrammingError提示 “can’t adapt type XXX”:传了不可序列化的对象(如 datetime.timezone)而没注册适配器
实操建议:
- 统一用位置参数(
$1,$2)写 asyncpg 查询,避免运行时解析开销 - psycopg[async] 若用
%(key)s,确保 dict 键名与 SQL 中完全一致,且不含空格或特殊字符 - 两者都禁用
f"SELECT * FROM {table_name}"这类拼接——asyncpg 会直接报错,psycopg 可能侥幸执行但埋下漏洞
批量插入时 asyncpg 的 executemany() 比 psycopg 快 3–5 倍,但需手动分块
asyncpg 的 executemany() 底层走的是二进制协议流式传输,psycopg[async] 的同名方法本质仍是循环调用单条 execute(),没真正批处理。
性能影响:
- 插入 10k 行,asyncpg 耗时约 80ms(分 1000 行/块),psycopg[async] 约 400ms(即使同样分块)
- 不分块直接塞 10k 行进 asyncpg,会触发内存暴涨甚至 OOM——它的缓冲区默认只撑 1k 行左右
实操建议:
- asyncpg 写批量插入务必用
await conn.executemany(sql, chunk),chunk 控制在 500–2000 行之间 - psycopg[async] 改用
await conn.cursor().executemany()并无加速效果,不如改用copy_records_to_table()(支持 CSV 流式导入) - 注意 asyncpg 不支持
RETURNING与executemany()同时使用——要返回 ID 就得切回单条 +fetchrow()
psycopg[async] 的类型自动转换更稳,asyncpg 需手动注册自定义类型
比如 PostgreSQL 的 jsonb、citext、enum,psycopg[async] 开箱即用;asyncpg 默认只认基础类型(int/str/bool/timestamp),遇到未知类型直接抛 asyncpg.exceptions.UndefinedObjectError 或返回 bytes。
容易被忽略的地方:
- asyncpg 的
register_json()只影响json,对jsonb无效——得用register_composite()或自己写 codec - psycopg[async] 的
set_client_encoding()是同步调用,不能 await;asyncpg 的等效操作是连接参数里的server_settings字典 - 两者对
bytea解码行为不同:psycopg 默认转bytes,asyncpg 默认转memoryview,直接比较会出TypeError
实操建议:
- 项目用到非标类型,优先测 asyncpg 的
conn.get_type_info()输出,再决定是否注册 codec - psycopg[async] 的
Row对象支持属性访问(row.id),asyncpg 返回的是Record,只能用索引或record['id'] - 别指望 asyncpg 自动把
numeric转成Decimal——它默认给float,精度丢失风险真实存在
事情说清了就结束











