copy from 是绕过 sql 引擎的底层写入通道,调优 wal 和事务配置(如 synchronous_commit=off、checkpoint_timeout=3600)可提速至 8.4 秒;配合流式 copy_from()、binary 格式及导入后 analyze/vacuum/索引重建,方达理论性能。

COPY FROM 不是“更快的 INSERT”,它是完全绕过 SQL 引擎的底层写入通道——用对了能快 20–50 倍,用错了可能比 INSERT 还慢。
为什么 COPY FROM 比 INSERT 快,但你没跑出理论速度?
根本卡点不在语法,而在 WAL 和事务配置:synchronous_commit = on 会让每次写入都等磁盘刷盘;checkpoint_timeout = 30s 会强制每半分钟做一次全量检查点,直接打断 COPY 流程。实测 100 万行 CSV,未调优时耗时 40 秒,调优后压到 8.4 秒。
-
synchronous_commit = off—— 仅限可接受崩溃丢失最后几秒数据的场景 -
checkpoint_timeout = 3600(1 小时),max_wal_size设为单次导入体积的 1.5 倍以上 -
wal_level必须为replica,不是logical(避免逻辑复制额外开销) - 临时禁用目标表的索引和外键约束,导入完成再重建;否则每行都触发索引更新
COPY FROM STDIN 在应用代码里怎么喂数据才不卡?
很多人用 copy_expert() 拼字符串,结果被 Python GIL 和内存拷贝拖垮。真正高效的是 copy_from() 配合流式迭代器,直接喂二进制或 CSV 行,不构造完整字符串。
- 用
csv.writer写入BytesIO,而非StringIO+encode(),减少编码转换 - 每批次控制在 10000–50000 行;小于 1000 行网络往返放大延迟,大于 100000 行易触发客户端内存暴涨
- 显式关闭 autocommit:
conn.autocommit = False;COPY 本身是原子操作,外层事务反而增加开销 - 别在循环里反复调用
copy_from();应一次性传入文件对象或生成器,让驱动内部流式处理
FORMAT binary 比 csv 快 2–3 倍,但要注意什么?
二进制格式跳过字段解析和类型推断,尤其含 timestamp、json、数组字段时更稳。但它要求客户端支持序列化,psycopg2 中需用 Binary() 包装字节流。
- CSV 要手动处理
NULL 'NULL'、DATEFORMAT、ENCODING 'UTF8'等隐形坑 - binary 格式不兼容 header 行,也不支持
FREEZE或LOG ERRORS选项 - 加
FREEZE可跳过后续 VACUUM(仅对本次插入生效),但只适用于无并发写入的场景 - 去掉
LOG ERRORS:错误日志本身会成为 I/O 瓶颈;先用小批量验证格式,再全量跑
导入后三件事,漏一件前面全白干
COPY 不触发 ANALYZE,也不更新 pg_class.reltuples,统计信息滞后会让后续查询走错执行计划。必须手动补上:
ANALYZE table_name;-
VACUUM table_name;(尤其没加FREEZE时) - 重建索引:
CREATE INDEX CONCURRENTLY ...(避免锁表)
最常被忽略的是统计信息更新——它不会自动发生,且无法通过重连或重启修复,只能显式执行。










