pgloader 完全指南:一条命令把 CSV、SQLite、MySQL、MSSQL 等任意数据源迁移到 PostgreSQL

原创2026-10-03 16:01:291,991 阅读
文章标签:ETL数据集成数据同步关系型数据库CLI数据工程

pgloader 完全指南:一条命令把 CSV、SQLite、MySQL、MSSQL 等任意数据源迁移到 PostgreSQL

pgloader 是一款面向 PostgreSQL 的数据装载与数据库迁移工具,其设计目标是让用户通过一条命令行(或一个 .load 命令文件)完成从文件或异构数据库到 PostgreSQL 的完整迁移,并支持边装载边转换数据、装载前后执行任意 SQL。读完本文,你将掌握 pgloader 支持的全部数据源与目标端、完整的特性矩阵,以及命令文件 DSL 与命令行两种驱动方式的核心用法,并能在 quickstart 与 command 等文档的配合下直接上手实战。

一、pgloader 是什么:基于 COPY 协议的流式装载器

根据 docs/intro.rst 的定位,pgloader 的核心能力可以概括为三点:

  • 多源装载:从各种来源读取数据并灌入 PostgreSQL;
  • 即时转换:对读取的数据在装载过程中“on the fly”进行转换;
  • 前后置 SQL:在装载之前与装载之后提交原始 SQL。

在传输层,pgloader 使用 PostgreSQL 的 COPY 协议将数据流式送入服务器。这一设计在源码中有明确对应:src/pg-copy/copy-rows-in-stream.lisp 中的 stream-rows-to-copy 通过 cl-postgres:open-db-writer 打开数据库写入端,然后在事务中逐行 stream-row 把数据推入 COPY 流,完成后再 COMMIT;发生错误时则 ROLLBACK 并更新统计信息。也就是说,pgloader 不是把数据整理成 SQL 语句再执行,而是复用 PostgreSQL 原生的、最高效的 COPY 二进制/文本协议通道。

与直接使用 COPY 相比,pgloader 的独特价值在于错误管理:它会将出错的数据与正常数据分离开来,通过填充一对 reject.dat 与 reject.log 文件来管理错误。其实现位于 src/utils/reject.lisp:

  • reject.dat:以 PostgreSQL COPY TEXT 格式记录被拒绝的数据行(%process-bad-row 中 write-sequence 写入原始行字节);
  • reject.log:以 UTF-8 追加记录 PostgreSQL 拒绝这些数据时抛出的错误信息。

这种“好数据照常装载、坏数据单独留档”的事务行为,正是 pgloader 相比裸 COPY/\copy 以及 Foreign Data Wrapper 的核心优势:PostgreSQL 默认事务语义下,输入数据中任何一行错误都会中断整张表的批量装载,而 pgloader 会把错误的影响范围控制在单个批次内。

二、数据源矩阵:文件与数据库全覆盖

pgloader 能识别并读取多种数据来源,按大类可分为文件与数据库两类(详见 docs/intro.rst):

文件类(Files)

  • CSV:逗号/制表符等分隔的文本文件,可配置表头、引号、转义、编码等;
  • Fixed Format:定宽格式文件,通过字段长度描述来解析;
  • Postgres COPY text format:PostgreSQL 自身的 COPY 文本格式(copy 源);
  • DBF:dBase 系列数据库文件,可附带 DBT memo 文件;
  • IXF:IBM 集成交换格式(Integration Exchange Format)。

数据库类(Databases)

  • SQLite(含自动发现表、索引、外键);
  • MySQL / MariaDB;
  • MS SQL Server;
  • PostgreSQL(支持 PostgreSQL 到 PostgreSQL 的迁移);
  • Redshift。

值得注意的细节:文件类源与数据库类源的驱动方式完全不同——文件源通常需要用户完整描述输入文件的字段属性(分隔符、列定义、编码等),而数据库源则由 pgloader 直接连接在线服务,自动获取元数据(表结构、索引、约束、类型定义),无需人工描述。这也是为什么从 SQLite 迁移一行命令即可完成,而装载一个裸 CSV 文件往往要给出 --field/--with 等参数。

在源码层面,src/api.lisp 中的 *get-code-for-source* 注册表把每种连接类型映射到对应的装载代码生成函数,涵盖 copy-connection、fixed-connection、csv-connection、dbf-connection、ixf-connection、sqlite-connection、mysql-connection、mssql-connection 与 pgsql-connection,与文档列出的数据源一一对应。

三、目标端:任何讲 PostgreSQL 协议的产品

pgloader 通过 PostgreSQL 协议向目标端写数据,因此凡是支持该协议的产品都可以作为目标:

  • PostgreSQL:标准目标端;
  • Citus:PostgreSQL 的分布式扩展,pgloader 支持把数据分布到 Citus(功能矩阵中的 “Distribute to Citus”);
  • Redshift:Amazon Redshift 兼容 PostgreSQL 线协议,可作为目标端。

需要说明的是,以上目标端信息来自 docs/intro.rst,其中 Citus 相关实现可进一步参考 src/pgsql/pgsql-ddl-citus.lisp 与 clojure/src/pgloader/ddl/citus.clj。

四、功能矩阵:按源数据库的能力对照表

pgloader 对不同源数据库的自动化程度与功能支持并不一致。docs/intro.rst 给出了官方功能矩阵,这里完整整理为 Markdown 表格(✓ 表示支持,✗ 表示当前未实现但技术上可添加,空单元格表示该特性对对应源没有意义):

功能(Feature) SQLite MySQL MS SQL PostgreSQL Redshift
One-command migration ✓ ✓ ✓ ✓ ✓
Continuous Migration ✓ ✓ ✓ ✓ ✓
Schema discovery ✓ ✓ ✓ ✓ ✓
Partial Migrations ✓ ✓ ✓ ✓ ✓
Schema only ✓ ✓ ✓ ✓ ✓
Data only ✓ ✓ ✓ ✓ ✓
Repeatable (DROP+CREATE) ✓ ✓ ✓ ✓ ✓
User defined casting rules ✓ ✓ ✓ ✓ ✓
Encoding Overrides ✓
On error stop ✓ ✓ ✓ ✓ ✓
On error resume next ✓ ✓ ✓ ✓ ✓
Pre/Post SQL commands ✓ ✓ ✓ ✓ ✓
Post-Schema SQL commands ✗ ✓ ✓ ✓ ✓
Primary key support ✓ ✓ ✓ ✓ ✓
Foreign key support ✓ ✓ ✓ ✓
Online ALTER schema ✓ ✓ ✓ ✓ ✓
Materialized views ✗ ✓ ✓ ✓ ✓
Distribute to Citus ✗ ✓ ✓ ✓ ✓

关于矩阵的两点官方解读:

  1. 部分缺失支持只是因为该特性对其它源“没有需求”,而非技术障碍——例如只有 MySQL 需要 Encoding Overrides(覆盖表或列的编码元数据),因为列表中的源里 MySQL 是唯一无法完全保证文本编码的;
  2. 同理,Redshift 不支持外键,因此 Foreign key support 一列为空。

矩阵中“Continuous Migration(持续迁移)”特别值得一提:得益于数据库源可以直接在线读取,pgloader 支持无人值守的在线迁移(Live Migration),一次命令同时处理 schema 与数据两部分,从而实现持续迁移模式。

五、两种驱动方式:命令文件 DSL 与命令行直连

pgloader 实现了自己的 Command Language——一种用于描述数据装载与迁移全过程的 DSL。不过你并非总需要写命令文件,docs/intro.rst 给出了命令行接受的两种变体:

pgloader [<options>] [<command-file>]...
pgloader [<options>] SOURCE TARGET
  • 第一种:提供一个或多个 .load 命令文件,文件中用 pgloader 命令语言描述迁移规格;
  • 第二种:直接在命令行给出数据来源(Source)与 PostgreSQL 数据库连接的目标(Target)。

这一分支逻辑在源码 src/main.lisp 的 main 函数中清晰可见:当剩余参数数量为 2 时调用 process-source-and-target 走“SOURCE TARGET”路线;否则走 process-command-file 读取命令文件。而 src/api.lisp 的 process-source-and-target 会解析来源 URI、目标 URI,以及 --type、--encoding、--set、--with、--field、--cast、--before、--after 等命令行选项,最终调用统一的 load-data 入口。

命令文件的 DSL 语法在 docs/command.rst 中有完整定义,其主干结构如下:

LOAD <source-type>
     FROM <source-url>
   [ HAVING FIELDS <source-level-options> ]
     INTO <postgresql-url>
   [ TARGET TABLE [ "<schema>" ]."<table name>" ]
   [ TARGET COLUMNS <columns-and-options> ]

[ WITH <load-options> ]

[ SET <postgresql-settings> ]

[ BEFORE LOAD [ DO <sql statements> | EXECUTE <sql file> ] ... ]
[  AFTER LOAD [ DO <sql statements> | EXECUTE <sql file> ] ... ]
;

核心子句为 LOAD、FROM、INTO、WITH;部分命令还实现 SET 子句,或 CAST 等源特有子句。DSL 解析器位于 src/parsers/command-parser.lisp,其 command 规则覆盖 load-archive、load-csv-file、load-fixed-cols-file、load-copy-file、load-dbf-file、load-ixf-file、load-pgsql-database、load-mysql-database、load-mssql-database、load-sqlite-database 等全部命令类型,命令之间以分号(;)分隔。

5.1 FROM:定义数据从哪来

FROM 子句指定数据来源,每种命令有自己的变体。例如 CSV 源支持 inline、stdin、普通文件名、带引号的文件名以及 FILENAME MATCHING 子句;而 MySQL 源只接受 MySQL 数据库 URI。

5.2 INTO:定义数据往哪去

PostgreSQL 连接 URI 中必须包含目标表名(可带 schema 限定),且目标表需要预先在 PostgreSQL 中创建。INTO 还支持可选的逗号分隔目标列列表,每列可以是输入字段名,也可以是“目标列名 + PostgreSQL 数据类型 + USING 表达式”的空白分隔组合:

  • USING 表达式可以是任意合法的 Common Lisp 形式,求值时包上下文为 pgloader.transforms,因此可以使用该包内定义的函数(包括通过 --load 动态加载的函数);
  • 每个 USING 表达式在运行时被编译为原生代码;
  • 该机制允许 pgloader 把 CSV 中任意数量的字段投影为数据库中任意数量的列。

5.3 WITH:装载选项

WITH 子句提供一组选项,全局语法为 key = value、use option 或 do not use option 三种形式。所有数据源通用选项包括:

  • on error stop / on error resume next
  • batch rows = R
  • batch size = ... MB
  • prefetch rows = ...

另有并行度相关设置:

  • workers = W
  • concurrency = C
  • max parallel create index = I
  • chunk size = N MB

5.4 SET:PostgreSQL 会话参数

SET 子句允许为 pgloader 打开的所有会话设置 session 参数,语法为参数名 + 等号 + 单引号包裹的值,多个参数用逗号分隔。参数名与取值 pgloader 不做校验,原样透传给 PostgreSQL。

5.5 BEFORE / AFTER LOAD:前后置 SQL

  • BEFORE LOAD DO:装载数据前执行 SQL,最常见的场景是 CREATE TABLE IF NOT EXISTS。每条命令必须用美元符($$)包裹(dollar-quoted),多条查询以逗号分隔;
  • BEFORE LOAD EXECUTE:同上,但从 SQL 文件读取查询,支持 PostgreSQL dollar-quoting 以及 \i、\ir 包含指令(同 psql 批处理模式);
  • AFTER LOAD DO / AFTER LOAD EXECUTE:装载完成后执行,是创建索引、约束或重新启用触发器的恰当时机;
  • AFTER CREATE SCHEMA DO / AFTER CREATE SCHEMA EXECUTE:schema 由 pgloader 创建之后、数据装载之前执行,适合做 ALTER TABLE、自定义扩展(如分区)等。

六、命令行选项速览

除了命令文件,pgloader 也支持完全从命令行驱动一次装载。常用选项(完整定义见 docs/pgloader.rst,实现见 src/main.lisp 的 *opt-spec*):

查询类(不装载数据)

  • --help:显示用法后退出;
  • --version:显示版本后退出;
  • --list-encodings:列出本版本已知的编码;
  • --upgrade-config:把 2.x 时代的 pgloader.conf(INI 语法)转换为新的命令语法输出到标准输出。

通用类

  • --verbose / --quiet / --debug:控制日志详细程度;
  • --root-dir:输出根目录(默认 /tmp/pgloader),reject 文件、日志等都在其下;
  • --logfile:日志文件路径(默认 /tmp/pgloader/pgloader.log);
  • --log-min-messages / --client-min-messages:分别过滤写入日志文件与输出到控制台的消息级别;
  • --summary:把汇总输出复制到指定文件,扩展名为 .csv、.copy 或 .json 时输出对应机器可读格式;
  • --dry-run:只检查连接、不真正装载数据,适合调试 .load 文件;
  • --on-error-stop:一旦 PostgreSQL 拒绝任何数据就停止,便于调试转换函数与类型转换规则;
  • --no-ssl-cert-verification:接受本地签发的服务端证书,绕开 SSL verify error: 20 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY(正确做法仍是使用受信证书)。

命令行专用类(配合 SOURCE TARGET 使用)

  • --with <option>:等价于命令文件的 WITH 子句,可多次使用;
  • --set "guc_name='value'":设置 PostgreSQL 会话参数,值的单引号不可省略;
  • --field <spec>:定义源文件字段,可逐字段多次使用,也可用逗号分隔多个字段定义(等价于 HAVING FIELDS 子句);
  • --cast <rule>:设置特定的类型转换规则;
  • --type <csv|fixed|db3|ixf|sqlite|mysql|mssql>:当 SOURCE 无法被自动识别时强制指定源类型;
  • --encoding <encoding>:指定源文件编码;
  • --before <file> / --after <file>:从 SQL 文件中解析查询并在装载前/后执行(查询需以分号结尾,支持 \i、\ir 包含)。

6.1 连接串格式

文件类来源连接串形如:

format:///absolute/path/to/file.ext
format://./relative/path/to/file.ext

其中 format 可取 csv、fixed、copy、dbf、db3、ixf;数据库类来源形如:

db://user:pass@host:port/dbname

其中 db 可取 sqlite、mysql、mssql。对于文件类源,pgloader 还原生支持从 HTTP 位置取文件并在需要时解压归档,但此时必须用 --type 显式指定文件格式(详见 docs/pgloader.rst)。

目标端连接串使用 PostgreSQL 连接 URI 格式:

postgresql://[user[:password]@][netloc][:port][/dbname][?option=value&...]

各组成部分的规则(docs/command.rst):

  • user:可含任意字符,冒号需双写(::),@ 需双写(@@);省略时依次回退到 PGUSER、USER 环境变量;
  • password:可含任意字符,@ 需双写(@@);省略时回退到 PGPASSWORD 环境变量,仍未设置则按 libpq 规则查找 .pgpass 文件(可用 PGPASSFILE 覆盖路径,Unix 下默认为 ~/.pgpass);
  • netloc:可以是域名、IPv4 或 Unix 域套接字路径;可通过 unix:/path:port/dbname 强制指定套接字路径与端口,路径中的冒号双写即可转义;
  • dbname:应为合法标识符;省略时回退到 PGDATABASE,再回退到用户名;
  • options:name=value 形式、& 分隔;支持 tablename(可带 schema 限定)、sslmode、host、port、dbname、user、password;sslmode 可取 disable、allow、prefer、require、verify-full。为向后兼容,tablename 也可直接写在 URI 的 ? 之后而不带 tablename= 前缀。选项会覆盖 URI 主组件。

七、快速上手:三条最典型的命令

结合 docs/quickstart.rst,以下三条命令展示了“一条命令完成迁移”的实战形态。

从 SQLite 全量迁移(自动发现表、索引、外键,类型转换后迁移数据):

createdb newdb
pgloader ./test/sqlite/sqlite.db postgresql:///newdb

从 MySQL 迁移(含 schema、数据,单行命令完成):

createdb pagila
pgloader mysql://user@localhost/sakila postgresql:///pagila

从 CSV 文件装载到已存在的表(全部参数走命令行):

pgloader --type csv                                   \
         --field id --field field                     \
         --with truncate                              \
         --with "fields terminated by ','"            \
         ./test/data/matching-1.csv                   \
         postgres:///pgloader?tablename=matching

注意 CSV 示例中:由于没有命令文件,--type、--field、--with 等附加信息必须全部在命令行给出;目标表必须已存在,且 PostgreSQL URI 通过 ?tablename= 携带目标表名(源数据见 test/data/matching-1.csv)。

其他实用变体:

  • 从标准输入读取:源用 - 表示 stdin,可配合 Unix 管道流式装载,例如 gunzip -c source.gz | pgloader --type csv ... - pgsql:///target?foo;
  • HTTP 远程 CSV:直接把 URL 作为来源传给 pgloader,同时配合 --field "usps,geoid,..." 与 --with "skip header = 1" 等选项;
  • 流式处理 HTTP 压缩文件:curl <url.gz> | gunzip -c | pgloader --type csv ... - postgresql:///pgloader?districts_longlat——pgloader 默认会先把 HTTP 内容下载到本地再解压处理,而管道方式让操作系统负责网络与命令间的流式缓冲,pgloader 只负责把数据流式灌入 PostgreSQL;
  • 远程归档 DBF:pgloader --type dbf <http-url-of-zip> postgresql:///foo,pgloader 会先下载、再解压、然后自动发现 schema 并装载数据——此时无法自动猜测源类型,必须显式 --type dbf。

八、深入原理:批处理、错误恢复与并行模型

为了在享受 COPY 速度的同时规避其“全有或全无”的缺陷,pgloader 把数据切成批次处理(详见 docs/batches.rst):

  • 默认每个批次 25000 行,批次在内存中保留,以便出错时重试;
  • 当 PostgreSQL 拒绝整个批次时,pgloader 解析失败 COPY 的 CONTEXT 错误信息(其中包含批内行号,如 CONTEXT: COPY errors, line 3, column b: "2006-13-11"),据此重试更小的批次:先重载错误行之前的所有行,记录错误行为 rejected,再尝试装载剩余部分,直到找出全部坏行;
  • 装载结束时,root-dir 下会生成以目标数据库命名的目录,其中以目标表名为文件名、.dat/.log 为扩展名的一对文件分别保存被拒数据(PostgreSQL COPY TEXT 格式)与客户端侧日志。

on error stop 与 on error resume next 控制是否启用批处理错误恢复:文件类装载(CSV、IXF、DBF)默认 on error resume next;而从其它 RDBMS 迁移时通常希望过程可复现,因此 SQLite、MySQL、MS SQL 源默认 on error stop,便于先修正 cast 规则、转换函数或输入数据,直到迁移完整跑通。

性能与并行模型:pgloader 采用经典的 Unix 管道模型——一个读取线程负责读数据(读 CSV、查 MySQL 等)并把预处理后的数据推入队列,另一个写入线程从队列取出数据、应用变换、以 COPY 协议批量写入 PostgreSQL。在此基础上:

  • workers 控制同时复制多少张表:数据库源默认 4,文件源默认 8;
  • concurrency 控制单表内的并行度:即每张表启动多少条独立的“读→写”流水线,默认 1;要求大于 1 且源支持范围分区(MySQL 按主键范围,PostgreSQL 14+ 按 ctid 块范围),每个并行 reader 配专属 writer,按 chunk size(默认 50 MB)切分不相交的源表区间;
  • max parallel create index 限制并行 CREATE INDEX 线程数(默认按源 schema 中单表最大索引数),这些线程只等待 PostgreSQL 完成实际工作,不计入 workers/concurrency 配额。

文档也坦诚指出:若给的是 PostgreSQL COPY 命令本身就能解析、且不含坏数据的文件,pgloader 永远不会比直接使用 COPY 更快——它的价值在于转换、容错与自动化,而非在单文件最佳场景下挑战裸 COPY 的极限速度。

九、进阶特性一览

  • 模板化命令(Mustache):命令文件支持 Mustache 模板系统,可通过 OS 环境变量注入变量,例如 DBPATH=sqlite/sqlite.db pgloader ./test/sqlite-env.load 配合 from '{{DBPATH}}' 使用;也可通过 --context ./test/sqlite.ini 把 INI 文件中的键值作为模板上下文(docs/command.rst);
  • 正则表达式:若干子句接受正则,规则为以 ~ 开头、后随成对分隔符(~//、~[]、~{}、~()、~<>、~""、~''、~||、~##),选择不与表达式冲突的分隔符对;
  • 注释:命令中支持 -- 行注释与 /* */ 块注释,任何允许空白的位置都允许注释;
  • 类型转换规则(CAST):通过 --cast 或命令文件 CAST 子句定义用户自己的类型转换规则,是数据库迁移正确性的关键开关。

结语

从 docs/intro.rst 这张“总纲”出发可以看到:pgloader 的设计哲学是“一个命令、多源输入、协议直通、错误隔离”。文件源靠用户描述、数据库源靠自动发现,目标端统一走 PostgreSQL 协议;装载过程可转换、可批处理、可并行、可重试。配合仓库内的 quickstart、command、batches、pgloader 等文档,以及 src/api.lisp、src/main.lisp 等核心实现,无论是简单的 CSV 灌库还是复杂的 MySQL 全量迁移,你都能用一条命令或一个 .load 文件稳定落地。

登录后查看全文
pgloader