psycopg3 中 executemany() 默认仅为客户端循环调用 execute(),无性能优势且易致超时或内存飙升;应改用 server_side=True、execute_batch()(仅支持 %s 占位符)或 execute_values()(支持 RETURNING)。

psycopg3 里 executemany() 为什么不能直接用?
因为 executemany() 在 psycopg3 中默认不启用服务器端批量执行,它只是客户端循环调用 execute(),既没性能优势,又容易在大批次时触发连接超时或内存飙升。你看到的“批量插入变慢”或 OperationalError: server closed the connection unexpectedly,大概率是这个原因。
实操建议:
- 明确改用
executemany()的server_side=True参数(需 PostgreSQL ≥ 14 + psycopg3 ≥ 3.1) - 确保连接已启用
prepare_threshold(默认为 5,可保留) - 避免传入含
None的元组——psycopg3 对空值绑定更严格,会报TypeError: can't adapt type 'None',应提前转成NULL或用sql.Null
用 execute_batch() 更稳妥,但要注意参数顺序
execute_batch() 是 psycopg3 官方推荐的批量插入方式,它把多条语句合并为单个网络往返,且自动处理类型适配和空值。但它不支持返回结果集(比如 RETURNING),也**不支持命名参数**——只能用位置占位符 %s。
常见错误现象:写成 "INSERT INTO t (a, b) VALUES (%(a)s, %(b)s)" 会直接抛 ProgrammingError: named parameters not supported in execute_batch。
立即学习“Python免费学习笔记(深入)”;
调用 Cutout.Pro 视觉处理 API 进行背景移除、人像抠图和照片增强,支持文件上传与图片 URL 输入。
实操建议:
- SQL 模板必须用
%s,例如:"INSERT INTO users (name, email) VALUES (%s, %s)" - 数据必须是序列(
list或tuple)的列表,每个子项长度要与 SQL 中占位符数一致 - 控制批次大小,建议
page_size=1000(太大易 OOM,太小失去批量意义) - 配合
cursor.execute("BEGIN")手动事务包裹,避免每批自动提交开销
需要 RETURNING 或动态字段?得用 execute_values()
当你要插入后立刻拿到自增 ID、或表结构不固定(比如 JSON 字段动态拼接)、或字段名/数量随数据变化时,execute_values() 是唯一选择。它底层用 VALUES 构造单条多行语句,效率接近原生 COPY,且支持 RETURNING。
容易踩的坑:
- 字段名必须显式传入
columns参数,不能靠数据推断;漏写会导致列数错位 - 如果某行某个字段为
None,对应位置必须传sql.Null(来自psycopg.sql),不能直接放None - 不支持混合类型插入(如一行 int、下一行 str 放同一列),类型必须统一,否则报
DataError: invalid input syntax - 注意
fetch=True仅在带RETURNING时有效,且返回的是list[Row],不是普通 tuple 列表
大批量插入前别忘了关掉 autocommit 和检查连接状态
psycopg3 默认 autocommit=False,但很多人在连接池里复用 cursor 时忘记重置事务状态,导致上一批失败后残留 INTRANS 状态,下一批直接报 ActiveSqlTransaction 错误。
性能关键点:
- 批量前显式执行
conn.autocommit = False(即使默认也是,防意外) - 用完记得
conn.commit()或出错时conn.rollback(),别依赖上下文管理器自动处理(它不保事务原子性) - 插入超 10 万行时,考虑用
copy_from()+ CSV 流(更快但需预处理成字符串流,不走 SQL 解析) - PostgreSQL 端确认
work_mem足够(至少 64MB),否则execute_values()可能因排序/哈希内存不足退化成慢路径
None、字段顺序错一位,错误信息往往不指向根源,而是报“无法解析参数”或“列数不匹配”。动手前先用 3 行数据硬编码跑通,再扩量。

















