使用 pgloader 将 MySQL 迁移到 PostgreSQL

pgloader 是一款开源的批量数据迁移工具,支持从 MySQL / SQLite / MSSQL / CSV 等多种数据源将表结构和数据导入到 PostgreSQL

一、镜像准备

# 也可以使用这个镜像:swr.cn-east-3.myhuaweicloud.com/r4in/dimitri/pgloader:3.6.9
docker pull ghcr.io/dimitri/pgloader:3.6.9

二、执行迁移

2.1 核心命令

也可以直接运行为容器,后续在容器中操作

在测试时,迁移 300w+ 数据大概使用5分钟

docker run --rm ghcr.io/dimitri/pgloader:3.6.9 pgloader \
  --dynamic-space-size 4096 \
  --with "workers = 2" \
  --with "concurrency = 1" \
  --with "prefetch rows = 1000" \
  --with "batch rows = 1000" \
  'mysql://<mysql_user>:<mysql_password>@<mysql_host>:3306/<mysql_db>' \
  'postgresql://<pg_user>:<pg_password>@<pg_host>:5432/<pg_db>'

上例中的 <mysql_user><mysql_password><mysql_host><pg_host> 等均为占位符,实际执行时替换为目标数据库的真实账号、密码、地址和库名即可。
注意:避免在密码中包含@这样的符号,否则可能会解析地址有问题

2.2 参数说明

参数说明
--dynamic-space-size 4096给 pgloader 的 SBCL Lisp 运行时分配 4096 MB 动态堆空间,大库迁移时防止堆溢出
--with "workers = 2"启用 2 个并行 worker 同时处理不同表
--with "concurrency = 1"每个 worker 的并发度为 1,适合源库读压力敏感的场景
--with "prefetch rows = 1000"每次从源库预取 1000 行到内存缓冲
--with "batch rows = 1000"每 1000 行批量写入目标库一次

workersconcurrency 根据源库负载和目标库写入能力进行调整。单库、表数量不多时上述默认值较为稳妥。

三、迁移后修正 Schema

pgloader 默认会把 MySQL 数据导入到与源库同名的 schema 中(例如 <mysql_db>)。如果目标 PG 库中业务代码默认使用 public schema,需要在迁移完成后做一次 schema 重命名。

3.1 检查现有 schema 和表

SELECT schemaname, tablename
FROM pg_tables
WHERE schemaname = 'public';

3.2 重命名 schema

BEGIN;

-- RESTRICT:如果 public 里存在对象会直接报错,不会误删
DROP SCHEMA public RESTRICT;

-- 将 pgloader 自动创建的 schema 重命名为 public
ALTER SCHEMA <mysql_db> RENAME TO public;

ALTER SCHEMA public OWNER TO postgres;
GRANT USAGE ON SCHEMA public TO PUBLIC;

COMMIT;

关键点:

  • DROP SCHEMA public RESTRICT 只在 public 为空时才能执行成功,避免误删已存在的对象。
  • ALTER SCHEMA ... RENAME TO public 把 pgloader 导入的 schema 重命名为业务使用的 public
  • 整个过程放在一个事务里,方便回滚。

3.3 验证

-- 列出 public schema 下所有表
\dt public.*

-- 校验数据行数
SELECT COUNT(*) FROM public.logs;
SELECT COUNT(*) FROM <mysql_db>.logs;

第二条 SELECT COUNT(*) FROM <mysql_db>.logs; 在 schema 重命名后应该报「schema 不存在」错误,说明重命名生效;第一条返回的行数应与迁移前源 MySQL 中对应表的行数一致。