copy是postgresql批量插入最快方式,吞吐量比insert高5–20倍,但需数据格式合规、服务端文件访问权或stdin流式传输;insert多值仅适合≤1000行;load data infile不适用于pg。

COPY 是 PostgreSQL 里最快的选择
在 PostgreSQL 中,COPY 几乎总是批量插入的首选——它绕过 SQL 解析层,直接走二进制或文本协议通道,吞吐量通常比 INSERT 高 5–20 倍。但前提是数据源可控、格式合规,且你有数据库写权限。
-
COPY必须由数据库服务端读取文件(如COPY table FROM '/path/to/file.csv'),除非用STDIN模式配合客户端流式传输 - 字段分隔符、NULL 表示(如
\N)、换行处理必须提前对齐,否则整批失败,错误提示常是invalid input syntax for type ... - 不支持 WHERE 过滤或表达式计算,所有转换得在导入前完成
- 事务内使用
COPY是原子的,但大文件可能撑爆内存;建议分批次,每批 ≤ 100MB
INSERT 多值语句只适合小批量(≤ 1000 行)
INSERT INTO t (a,b) VALUES (1,'x'),(2,'y'),(3,'z') 看似简洁,实际受协议开销和查询解析限制。超过几千行后,性能断崖下跌,还容易触发 statement timeout 或 out of memory。
- PostgreSQL 单条
VALUES列表最大支持约 10 万项(取决于字段数和长度),但不建议逼近上限 - 参数化时用
$1, $2占位符比字符串拼接安全,但驱动层可能把多值展开成多个独立INSERT,反而更慢 - 如果用 ORM(如 SQLAlchemy),确认它没把批量操作拆成单行
INSERT—— 查日志看实际发出的语句 - 无索引表下,1000 行以内
INSERT多值和COPY差距不大;但加了唯一索引后,COPY仍稳,INSERT多值会因逐行检查而明显变慢
LOAD DATA INFILE 是 MySQL 的专属方案,PG 不认
别在 PostgreSQL 里搜 LOAD DATA INFILE —— 它是 MySQL 语法,PostgreSQL 完全不识别,执行会报错 ERROR: syntax error at or near "LOAD"。有人误配连接池或迁移脚本时混用,结果卡在建表后第一行插入就失败。
- MySQL 的
LOAD DATA INFILE类似 PG 的COPY,但权限模型不同:MySQL 要求文件在服务端且用户有FILE权限;PG 的COPY FROM默认只允许超级用户读本地文件 - 跨数据库迁移时,别直接复制 SQL 脚本。用
pg_dump --inserts生成兼容 INSERT,或用psql \copy命令做客户端中转 - 如果必须从 CSV 导入且无服务端文件访问权,用
\copy(psql 客户端命令)替代COPY—— 它由客户端读文件再流式发给服务端,权限要求低,速度略降但可控
真正影响速度的,往往不是语句本身
很多人调优卡在选 COPY 还是 INSERT,却忽略 WAL、索引、约束这些“后台角色”。关掉它们能提速数倍,但必须清楚后果。
- 导入前执行
SET LOCAL synchronous_commit = off可跳过刷盘等待,但崩溃可能丢最近几秒数据 - 临时禁用索引:
ALTER TABLE t DROP CONSTRAINT ...或先CREATE INDEX CONCURRENTLY后建,比边插边更新快得多 - 外键和唯一约束无法“暂停”,只能删了再加;但加回时会全表扫描验证,大数据量慎用
- 如果表已有大量数据,
COPY前先VACUUM ANALYZE并确认fillfactor设置合理(如 70),避免频繁页分裂
最常被跳过的一步:确认客户端与服务端的 timezone 和 datestyle 一致。CSV 里一个 2023-01-01 在不同时区下可能被解析成不同日期,等查出数据错位再重导,时间早花光了。










