目标
一次一条地插九十万行,等于把网络往返、解析与计划各付九十万次。COPY 协议把数据当作一根连续管道流过去,零逐行开销,提速通常一到两个数量级。让它保持安全的模式不变:先落草稿表,再幂等 upsert,于是重跑与坏行都变成常规操作而非灾难。
步骤要点
- 先量化逐行插入基线:900 万次往返、40 分钟,这是重写必须打败的数字。
- 用 pg-copy-streams 把 CSV 经 pipeline 流入 COPY staging_events FROM STDIN (FORMAT csv)。
- 从 staging 用 ON CONFLICT (id) DO UPDATE 幂等并入目标表,崩溃重跑不丢已提交数据、不产生重复。
- 端到端计时:同样九十万行约一分钟,约 40 倍吞吐提升。
- 加载期间监控复制延迟;单个巨型 COPY 的 WAL 突发若影响副本,就把文件切成多个 COPY 事务分块。
排错提示
COPY 遇到坏行整批失败,应先载入约束宽松的 staging,清洗隔离坏行后再 upsert 幸存者;驱动不支持 COPY 就退回 psql \copy 文件方式,同样走 COPY 协议;重跑出现重复说明直接 COPY 进了目标表——务必先进 staging。另外 COPY 绕过应用层的逐行校验钩子,这部分责任由 staging 清洗承接。
适用边界
单个 COPY 即单个事务,超大导入仍需为复制与锁健康分块;staging 模式占用额外临时磁盘。
代码示例
const stream = client.query(
copyFrom("COPY staging_events (id, kind, payload, occurred_at) FROM STDIN WITH (FORMAT csv, HEADER true)")
);
await pipeline(fs.createReadStream("events.csv"), stream);
// Then upsert idempotently
INSERT INTO events ... SELECT ... FROM staging_events
ON CONFLICT (id) DO UPDATE SET payload = EXCLUDED.payload;