第一次接触数据库迁移时,我试过手动导出SQL再导入的方式,结果遇到各种数据类型不兼容的问题,光是处理日期字段的差异就花了整整两天。后来发现了Pgloader这个神器,才发现原来数据库迁移可以这么轻松。
Pgloader最让我惊艳的是它的"智能容错"特性。记得有次迁移一个包含200张表的电商数据库,其中有几张表存在非法日期数据(比如MySQL里常见的0000-00-00)。传统方式会直接报错中断,而Pgloader不仅自动将这些值转为PostgreSQL的NULL,还贴心地生成了错误报告文件。实测下来,百万级数据的迁移成功率能达到99.9%以上。
它的并行处理能力更是提升效率的关键。通过设置worker_count参数,我让8个CPU核心同时工作,原本需要6小时的迁移任务缩短到45分钟。对于需要停机维护的生产环境,这个时间优势太重要了。
根据我处理过的十几个迁移项目,建议准备:
最近一个客户案例中,我们用AWS的r5.2xlarge实例(8vCPU/64GB内存)迁移380GB的数据库,整个过程只用了2小时17分钟。
Ubuntu/Debian用户最方便:
bash复制sudo apt update
sudo apt install -y pgloader postgresql-client
CentOS/RHEL需要先添加EPEL源:
bash复制sudo yum install -y epel-release
sudo yum install -y pgloader
Docker方式适合快速测试:
bash复制docker run --rm -v /path/to/config:/config dimitri/pgloader \
pgloader /config/mysql_to_pg.load
注意:生产环境建议使用3.6.8以上版本,修复了多个内存泄漏问题
这是我经过多次优化后的标准模板:
lisp复制LOAD DATABASE
FROM mysql://user:pass@mysql-host:3306/source_db
INTO postgresql://user:pass@pg-host:5432/target_db
WITH
concurrency = 8,
batch size = 50000,
prefetch rows = 100000
CAST
type datetime when (<> "0000-00-00") to timestamptz,
type date when (<> "0000-00-00") to date drop not null
MATERIALIZE VIEWS ALL
INCLUDING ONLY TABLE NAMES MATCHING ~/orders/, ~/users/
BEFORE LOAD DO
$$ CREATE SCHEMA IF NOT EXISTS legacy; $$,
$$ SET search_path TO legacy,public; $$
性能关键参数:
| 参数名 | 推荐值 | 作用 |
|---|---|---|
| maintenance_work_mem | 1GB | 提升索引创建速度 |
| work_mem | 64MB | 每个操作的内存配额 |
| max_parallel_workers | 4 | 并行工作进程数 |
避坑经验:
SET text_search_path TO pg_catalogtype jsonb using convert-json-to-jsonbreset sequences参数sql复制-- MySQL端执行
SELECT version();
SHOW VARIABLES LIKE 'character_set%';
-- PostgreSQL端执行
SHOW server_version;
bash复制# 估算MySQL数据库大小
SELECT
table_schema "Database",
SUM(data_length + index_length) / 1024 / 1024 "Size (MB)"
FROM information_schema.TABLES
GROUP BY table_schema;
阶段一:结构迁移
lisp复制WITH
create tables no indexes,
no foreign keys
阶段二:数据迁移
lisp复制WITH
disable triggers,
no truncate
阶段三:建立关系
lisp复制AFTER LOAD DO
$$ CREATE INDEX ... $$,
$$ ALTER TABLE ... ADD CONSTRAINT ... $$
推荐使用pgloader的实时监控功能:
bash复制pgloader --client-min-messages debug mysql.load 2>&1 | tee migration.log
关键指标解析:
Bytes read:已迁移数据量import done:表完成百分比errors:需要重点关注的数字问题1:字符集不匹配
lisp复制SET PostgreSQL PARAMETERS
client_encoding to 'utf8'
问题2:外键冲突
lisp复制WITH
defer constraints,
batch rows = 1000
问题3:网络中断恢复
bash复制# 使用--resume参数继续中断的迁移
pgloader --resume /path/to/state.file mysql.load
我常用的验证SQL:
sql复制-- 行数比对
SELECT
(SELECT COUNT(*) FROM mysql_table) as mysql_count,
(SELECT COUNT(*) FROM pg_table) as pg_count;
-- 抽样校验
SELECT * FROM mysql_table ORDER BY RAND() LIMIT 100
EXCEPT
SELECT * FROM pg_table ORDER BY RANDOM() LIMIT 100;
使用pgbench进行迁移前后对比:
bash复制# MySQL基准
sysbench oltp_read_write --db-driver=mysql prepare
sysbench oltp_read_write --db-driver=mysql run
# PostgreSQL基准
pgbench -i -s 100 target_db
pgbench -c 20 -j 4 -T 300 target_db
对于TB级数据库,我推荐这样操作:
lisp复制-- 首次全量迁移
WITH truncate, create indexes
-- 后续增量
WITH
no truncate,
no create indexes,
include drop
在AWS RDS环境中,这些调整很有效:
lisp复制SET
rds.extensions to 'pg_stat_statements,btree_gin',
synchronous_commit to off
去年帮某跨境电商迁移时遇到个典型问题:他们的商品表有2.4亿条记录,包含大量JSON格式的规格参数。最终采用的方案是:
lisp复制INCLUDING ONLY TABLE NAMES MATCHING ~/products_202[0-3]/
lisp复制CAST
type json to jsonb using convert-json-to-jsonb
bash复制pg_comparator --verbose mysql://src pg://dst
整个迁移过程历时6小时18分钟,停机窗口控制在15分钟内,客户非常满意。关键是把batch size从默认的25000调整到50000,同时增加了maintenance_work_mem到2GB。