一条命令完成 PostgreSQL 数据迁移:pgloader 从零到一的实战指南
【免费下载链接】pgloaderMigrate to PostgreSQL in a single command!项目地址: https://gitcode.com/gh_mirrors/pg/pgloader
你有没有遇到过这样的场景:公司决定从 MySQL 迁到 PostgreSQL,表结构、索引、外键、几十万行数据,全部要手工搬。你打开 psql,一条条CREATE TABLE照着抄,再用\copy导数据,结果第 371 行一条非法日期就让整个导入报废。这是很多 DBA 都经历过的噩梦。
pgloader 就是为这个场景而生的 PostgreSQL 数据加载工具。它靠一条命令就能完成从 MySQL、SQLite、SQL Server、CSV、DBF、IXF 等多种数据源到 PostgreSQL 的迁移,更关键的是——它自带智能错误隔离机制,坏数据会被挑出来单独记进日志,好数据继续入库,整个迁移不会因为一行脏数据就崩盘。本文会带你从安装开始,完整跑通一次真实迁移,再讲透配置文件、常见报错和性能调优。
第一章 为什么是它:被脏数据支配的恐惧
先说说最扎心的痛点。PostgreSQL 的COPY命令走的是事务语义:只要输入数据里有一行出错,整个表的数据全部回滚。设想你导一个 100 万行的 CSV,跑到第 80 万行遇到一个格式不对的日期,前面 80 万行全部白干。你改完错误再来一遍,又卡在下一行脏数据上。
pgloader 彻底改写了这个剧本,它的设计哲学是"有错先记下来,别挡住大部队"。底层它把数据切成 25000 行一个的批次(batch),用 COPY 协议流式灌入;一旦 PostgreSQL 拒绝某个批次,它就从错误消息里解析出问题行号,把好行重新发一遍、坏行写进独立的reject.dat文件,然后继续往后加载。
| 关注点 | 传统 COPY / \copy | 用 pgloader 的收获 |
|---|---|---|
| 错误处理 | 一行出错,全表回滚 | 坏行隔离到 reject 文件,好行继续加载 |
| 数据源 | 只认 CSV 文本 | MySQL/SQLite/MSSQL/CSV/DBF/IXF/归档/HTTP 全支持 |
| 库结构迁移 | 手写 DDL,逐表抄 | 自动发现表、索引、外键、注释、序列 |
| 数据类型 | 手工预处理 | 内置 CAST 规则,零日期自动转 NULL |
| 重复执行 | 每次都要手工清理 | 默认 DROP+CREATE,可重复跑直到通过 |
别忘了它还有一个杀手锏:它可以连数据库本身一起迁。所谓 "single command" 不是只迁数据,而是 schema(表结构、索引、外键、注释)和数据一锅端。这背后是它先去源库翻目录(catalog)拿到全部结构定义,做类型映射后再开始搬数。
第二章 动手前准备:环境要求与安装清单
pgloader 目前有两个版本线,请按你的情况选。
版本说明
- v4(推荐):基于 Clojure/JVM 的完整重写,发布为单个 JAR 文件,要求 Java 21 或更高版本。无原生依赖、无共享库,大迁移时内存溢出问题通过 JVM 堆参数(
-Xmx)解决。 - v3:Common Lisp 老版本,Debian/Ubuntu 自带
apt-get install pgloader即可安装。
✅ 版本兼容提示:v4 完全兼容 v3 的
.load命令文件语法和命令行参数,旧配置文件可以直接拿来用。
安装清单
方式一:下载 v4 预编译 JAR(推荐,两步搞定)
# 1. 下载 JAR(官方发布页始终指向最新构建) curl -L -o pgloader.jar https://github.com/dimitri/pgloader/releases/download/v4-dev/pgloader.jar # 2. 验证版本 java -jar pgloader.jar --version如果你想全局使用pgloader命令,可以加一个包装脚本:
sudo install -m 755 pgloader.jar /usr/local/lib/pgloader.jar echo '#!/bin/sh\nexec java -jar /usr/local/lib/pgloader.jar "$@"' | sudo tee /usr/local/bin/pgloader sudo chmod 755 /usr/local/bin/pgloader pgloader --version方式二:Docker 一行跑起来
docker pull ghcr.io/dimitri/pgloader:latest docker run --rm -it ghcr.io/dimitri/pgloader:latest pgloader --version方式三:Debian/Ubuntu 用 v3 系统包
sudo apt-get install pgloader方式四:从源码编译(所有发行版通用)
git clone https://gitcode.com/gh_mirrors/pg/pgloader cd pgloader make ./build/bin/pgloader --help # 产物在这里动手前自查清单
- ✅ 本地能连上目标 PostgreSQL(
createdb、psql可用) - ✅ 源数据所在路径有读权限;从数据库迁移时源库账号有读权限
- ⚠️ 确认目标库已创建,或确认
include drop, create tables等选项生效 - ⚠️ 大文件迁移前先看一眼磁盘剩余空间(reject 文件会占额外空间)
- ✅ 跑任何命令前先执行
pgloader --help看当前版本的参数列表
第三章 一次完整的实战:把 SQLite 库搬进 PostgreSQL
我们选 SQLite 作为第一个实战,因为它是"零前置配置"的最好示范——不需要账号密码,一个.db文件就是整个数据库。这也是 pgloader 文档里的官方入门案例。
第一步:创建目标数据库
createdb newdb这一步在做什么:pgloader 负责建表、建索引、导数据,但"数据库本身"这个壳得先存在。就像搬家,箱子 pgloader 帮你打包,但新房子门要先开。
第二步:一条命令完成全部迁移
pgloader ./test/sqlite/sqlite.db postgresql:///newdb是的,就这么一行。pgloader 会依次做四件事:
- 打开 SQLite 文件,通过它的系统目录自动发现所有表定义
- 把 SQLite 的数据类型映射成 PostgreSQL 类型(CAST 规则)
- 在 PostgreSQL 里建表、建索引、建外键
- 用 COPY 协议批量灌入数据
注意目标连接串postgresql:///newdb——三个斜杠代表"本地默认端口 + 默认用户 + 数据库 newdb",这是简写,完整写法是postgresql://user:pass@localhost:5432/newdb。
第三步:MySQL 迁移也一个套路
如果源是 MySQL,逻辑完全一样,只是把连接串换成 MySQL 的:
createdb pagila pgloader mysql://user@localhost/sakila postgresql:///pagilapgloader 会自动发现 sakila 库里的表、索引、外键、注释,全部翻译成 PostgreSQL 版本,然后并行搬数据。
第四步:看看迁移到底干了啥
想确认配置没写错、连接能通、又不真想动数据?用干跑模式:
pgloader --dry-run my-migration.load想把过程记成日志、把结果汇成报告?加两个参数:
pgloader --verbose --logfile migration.log --summary report.txt migration.load--summary支持.csv、.json、.copy后缀,机器可读,方便接进监控看板。
为什么它能自动建表?——"发现式迁移"
如果你好奇背后的原理:pgloader 迁移数据库时,会先连接源库的元数据目录,把表清单、字段类型、默认值、非空约束、主键、外键、索引、注释全部读进一个内部 catalog,再据此生成 PostgreSQL 的 DDL。所以"一条命令"不是黑魔法,而是它替你干了原本手工抄 DDL 的活。
第四章 让它更懂你:配置文件与自定义转换
命令行适合简单场景,但真实世界的数据总是脏的。当你需要指定目标表、做类型转换、迁移前建 schema、迁移后建索引时,就该上.load命令文件了。
一个足够完整的配置文件模板
LOAD DATABASE FROM mysql://root@localhost/sakila INTO postgresql://localhost:54393/sakila WITH include drop, create tables, create indexes, reset sequences, workers = 8, concurrency = 1, multiple readers per thread, rows per range = 50000 SET PostgreSQL PARAMETERS maintenance_work_mem to '128MB', work_mem to '12MB' SET MySQL PARAMETERS net_read_timeout = '120', net_write_timeout = '120' CAST type bigint when (= precision 20) to bigserial drop typemod, type date drop not null drop default using zero-dates-to-null, type year to integer ALTER SCHEMA 'sakila' RENAME TO 'pagila' BEFORE LOAD DO $$ create schema if not exists pagila; $$; AFTER LOAD DO $$ analyze pagila.*; $$;逐段拆解:这份配置在说什么
| 段落 | 作用 | 本例含义 |
|---|---|---|
WITH include drop | 先删后建 | 目标表存在则先 DROP,保证可重复执行 |
create tables, create indexes | 迁移结构 | 自动建表和索引 |
reset sequences | 重置自增 | 序列从迁移后的最大值续走 |
workers / concurrency | 并发控制 | 8 个 worker 同时迁移多张表 |
SET ... PARAMETERS | 会话参数 | 迁移期间调大 work_mem 加速排序 |
CAST ... | 类型转换 | 见下文详解 |
BEFORE / AFTER LOAD | 前后钩子 | 迁移前建 schema,迁移后收集统计信息 |
CAST 规则:把 MySQL 的"任性"翻译成 PostgreSQL
CAST是 pgloader 最值钱的自定义能力。举个经典例子:MySQL 的日历里存在公元 0 年(0000-00-00),而 PostgreSQL 直接拒绝这种非法日期。pgloader 内置了一条规则把这些零日期自动转成NULL:
CAST type date drop not null drop default using zero-dates-to-null再看几个常用写法:
-- 把 MySQL 的 unsigned bigint(20) 翻译成自增 bigserial CAST type bigint when (= precision 20) to bigserial drop typemod, -- tinyint 不转布尔(默认会转),改成整数 -- type tinyint to boolean using tinyint-to-boolean, -- 注释掉即可停用 CAST type year to integer,你还可以针对具体列下规则,比如把base64.data列解码成jsonb、把身份证列统一转成uuid。规则粒度从"某种类型"到"某个表的某列"都支持,够灵活。
第五章 避坑锦囊:高频报错一次说清
迁移踩坑是必经之路,这里把我见过的高频问题按"症状 → 根因 → 解法"整理给你。
坑 1:一到某行就报错,整个导入失败
- 症状:加载 CSV 中途报
COPY errors, line 3, column b: "2006-13-11",然后全部回滚。 - 根因:文件类数据源默认
on error resume next是开启的,理论上不会全挂;你会遇到全挂,多半是手滑开了on error stop。 - 解法:确认
WITH on error resume next(文件源默认值)。数据库源迁移默认是on error stop,因为重跑迁移更安全,但如果你想"坏了就跳过",显式加WITH on error resume next。
坑 2:提示 Java 版本不对,JAR 跑不起来
- 症状:
java -jar pgloader.jar报UnsupportedClassVersionError或类似错误。 - 根因:pgloader v4 强制要求Java 21+,你的机器装的是老 JDK。
- 解法:先
java -version确认版本,升级到 Temurin 或 OpenJDK 21。装好后再java -jar pgloader.jar --version验证。
坑 3:MySQL 连不上,报 SSL 或认证错误
- 症状:
mysql://root:pass@db:3306/mydb连接失败。 - 根因:新版 MySQL(尤其 8.x)默认走 SSL 或要求 RSA 公钥交换,常见于本地 Docker 环境。
- 解法:连接串加参数显式关掉 SSL。v4 支持原生 URI 和标准 JDBC URL 两种写法:
pgloader 'mysql://root:pass@db:3306/mydb?useSSL=false' postgresql:///target pgloader 'jdbc:mysql://root:pass@db:3306/mydb?useSSL=false' postgresql:///target坑 4:导出乱码,中文全变问号
- 症状:迁移后中文显示乱码。
- 根因:源数据的实际编码和库表元数据里声明的不一致(MySQL 常见),或目标连接默认字符集不对。
- 解法:用
DECODING TABLE NAMES MATCHING覆盖声明,或迁移前在SET里固定 client_encoding:
SET client_encoding to 'latin1'坑 5:迁移被中断,不知道死在哪
- 症状:跑到一半报错退出,找不到问题行。
- 根因:不想让坏行干扰排查。
- 解法:排查阶段用
--on-error-stop,让它在第一条被拒数据处停下,配合--logfile看完整上下文;修好之后再切回默认继续模式跑全量。
第六章 性能与效率心得:把大迁移跑出高水位
pgloader 底层是"读线程 → 队列 → 写线程 → COPY"的流水线模型,性能参数基本就是围绕这条流水线设置的。下面这些数字都是真实参数,可以照抄再按你的机器微调。
1. 批量参数:别让单批次过小
批处理机制默认 25000 行一个批次。批次越小,出错重试越精细,但吞吐也越低。文件干净的情况下可以调大:
WITH batch rows = 50000, batch size = 100MB, prefetch rows = 1000002. 并发参数:worker 不是越多越好
workers控制同时搬几张表:数据库源默认 4,文件源默认 8。concurrency控制单表内并行度(需大于 1,且源要支持按主键范围或 ctid 范围分片)。
WITH workers = 8, concurrency = 4, chunk size = 50MB注意:concurrency = 4意味着这张表同时跑 4 条独立的读→写流水线,各读各的分片,写是配对的。机器核数不够时别硬堆。
3. 索引策略:数据先进来,索引后建
迁移期间建索引很拖速度。合理做法是:
WITH create indexes, max parallel create index = 2或者干脆数据先导进来,用AFTER LOAD DO再建索引:
AFTER LOAD DO $$ create index idx_orders_created_at on orders (created_at); $$;4. 目标库参数:迁移时临时调大内存
排序、建索引都吃内存,迁移窗口内调大不影响平时:
SET maintenance_work_mem to '512MB', work_mem to '32MB'5. 关于极限速度的实话
文档里写得很实在:如果数据源本身是 COPY 能直接读的干净文件,pgloader 不会比原生 COPY 更快。它卖的不是极限速度,而是"脏数据也能扛 + 复杂转换也能做 + 全程一条命令"。想快,就保证源数据干净;想稳,就放心交给它。
第七章 从这里继续进阶:源码、文档与示例
项目源码和文档都在仓库里,按下面的索引去读,效率最高。
- 官方文档:docs/index.rst 是总入口,含全部手册;docs/quickstart.rst 讲最快上手路径
- 命令语法手册:docs/command.rst,
.load命令 DSL 的完整文法;批处理与并行机制看 docs/batches.rst - 各数据源手册:docs/ref/mysql.rst、docs/ref/sqlite.rst、docs/ref/mssql.rst、docs/ref/csv.rst
- 核心加载逻辑源码:src/load/ 是入口,src/pgsql/ 是 PostgreSQL 端实现
- 转换函数源码:src/utils/transforms.lisp,想自定义数据清洗可以照这里的写法扩展
- 真实可用示例:test/ 目录里全是官方
.load文件,test/mysql/my.load 是 MySQL 迁移的完整范例,test/csv.load 是 CSV 场景带注释的范本 - 测试数据:test/data/ 里有可以直接练手的 DBF、CSV 文件
结语:好的工具让迁移从项目变成例行公事
回到开头的那个噩梦:手工抄 DDL、逐行排错、一次失败全盘重来。有了 pgloader,这些都变成了它替你扛的细节。它不完美——极限吞吐比不上裸 COPY,遇到实在无解的数据还是要回到--on-error-stop逐条修——但它把"数据迁移"从一件让人头皮发麻的大工程,压缩成了一条可以写进 CI、每天夜里自动跑一遍的命令。文档里把那套做法叫Continuous Migration:天天迁、次次验证,直到全绿,然后择日切库,毫无惊吓。
给你的下一步行动建议很具体:今晚就createdb newdb,拿仓库里test/data/的一个 CSV 或 DBF 文件跑一次pgloader,五分钟内你就能亲眼看到"一条命令搬完一张表"到底是什么感觉。等你能把那张表跑通,再翻开 docs/command.rst 写你的第一个.load文件。工具不怕用不熟,就怕你还没开始。
【免费下载链接】pgloaderMigrate to PostgreSQL in a single command!项目地址: https://gitcode.com/gh_mirrors/pg/pgloader
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考