psycopg2的copy_from是最快路径,但需严格预处理数据格式;bulk_insert_mappings跳过orm实例化,兼顾性能与可控性;to_sql默认行为危险,须显式配置index、method等参数。

psycopg2 的 copy_from 是最快路径,但必须预处理数据格式;SQLAlchemy 的 bulk_insert_mappings 和 pandas.DataFrame.to_sql 是折中选择,各自有明确的适用边界。
用 copy_from 写入 PostgreSQL(最快,但最糙)
这是绕过 SQL 解析层、直写数据文件的底层方式,实测比 execute_values 快 2 倍以上。但它不校验类型、不触发触发器、不走连接池,错误信息也原始得多。
- 数据必须是制表符分隔、无 header、
null显式写成空字符串(''),否则会报psycopg2.errors.InvalidTextRepresentation - 字段顺序必须与表定义完全一致,大小写敏感——
copy_from不认列名映射 - 连接需调用
engine.raw_connection(),手动commit(),失败不会自动回滚 - JSON 字段得提前
json.dumps()成字符串,datetime得转成'2026-07-02 04:57:00'格式
bulk_insert_mappings 跳过 ORM 实例化(快且可控)
它把字典列表直接拼成 INSERT 语句,跳过 User(**d) 构造对象这一步,CPU 和内存开销明显下降,但仍走 SQLAlchemy 连接池和事务管理。
- 不支持
default或server_default,比如'created_at': datetime.now()必须显式塞进每个字典 - 单次传入超过 5000 条可能触发 PostgreSQL 的参数绑定上限(
too many arguments),建议控制在 1000–3000 条/批 - 字段名若含大写字母或特殊字符(如
"User_Name"),需确保字典 key 与数据库列名严格一致,否则静默丢字段
to_sql 看似省事,但默认行为很危险
很多人以为 df.to_sql(..., if_exists='append') 就完事了,结果上线后发现多出一列 index、字段顺序错乱、或者 MySQL 下慢得离谱。
-
index=False必须显式写上,否则 pandas 默认把 DataFrame index 当成一列写进去 - MySQL 下默认每行一条
INSERT,加method='multi'可合并为单条多值语句,提速 3–5 倍 - PostgreSQL 下别用
method='multi',它不兼容;应改用method=lambda _, df: pg_copy_from(df)配合自定义copy_from - 列名含大小写时,
to_sql会报ProgrammingError: (psycopg2.errors.UndefinedColumn),加schema='public'或统一小写列名可解
别忽略连接和事务控制这个“隐形瓶颈”
再快的写入方法,如果每次写都新建连接、每条都 commit,性能照样崩。实际部署时,psycopg2 的连接池配置、autocommit=False 手动控制提交时机、以及批量 size 的经验值(通常 1000–2000),比选哪种 API 更关键。
真正容易被忽略的是:COPY 和 copy_from 不走连接池,bulk_insert_mappings 和 to_sql 走;而一旦用了 to_sql 的 method 自定义函数,就又脱离了 SQLAlchemy 的事务封装——这些边界点,不测数据量、不看错误堆栈,根本发现不了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











