19 - 数据迁移
第 19 章 · 数据迁移
本章介绍 PostgreSQL 版本升级、从其他数据库迁移到 PG 以及常用迁移工具。
19.1 版本升级
升级方式对比
| 方式 | 停机时间 | 适用场景 | 风险 |
|---|
| pg_upgrade | 分钟级 | 大版本升级(推荐) | 低 |
| 逻辑复制 | 秒级 | 跨版本热迁移 | 中 |
| pg_dump/pg_restore | 小时级 | 小数据库 | 低 |
| pg_upgrade –link | 分钟级 | 超大数据库 | 中 |
pg_upgrade
# 1. 安装新版本
sudo apt install postgresql-17
# 2. 停止所有 PostgreSQL 服务
sudo systemctl stop postgresql
# 3. 检查兼容性
/usr/lib/postgresql/17/bin/pg_upgrade \
--old-datadir=/var/lib/postgresql/16/main \
--new-datadir=/var/lib/postgresql/17/main \
--old-bindir=/usr/lib/postgresql/16/bin \
--new-bindir=/usr/lib/postgresql/17/bin \
--check
# 4. 执行升级
/usr/lib/postgresql/17/bin/pg_upgrade \
--old-datadir=/var/lib/postgresql/16/main \
--new-datadir=/var/lib/postgresql/17/main \
--old-bindir=/usr/lib/postgresql/16/bin \
--new-bindir=/usr/lib/postgresql/17/bin \
--link # 使用硬链接,速度极快(但两版本共享数据目录)
# 5. 更新统计信息
analyze_new_cluster.sh
# 6. 启动新版本
sudo systemctl start postgresql-17
⚠️ 注意事项:使用 --link 时,旧版本数据目录会被链接到新目录,删除旧版本会破坏数据。必须先做完整备份!
19.2 pgloader(Oracle → PostgreSQL)
pgloader 是最流行的异构数据库迁移工具。
安装
sudo apt install pgloader
# 或 Docker
docker pull dimitri/pgloader:latest
MySQL → PostgreSQL
# 命令行
pgloader mysql://user:pass@mysql-host/mydb postgresql://user:pass@pg-host/mydb
# 配置文件(mysql.load)
LOAD DATABASE
FROM mysql://user:pass@mysql-host/mydb
INTO postgresql://user:pass@pg-host/mydb
WITH include drop, create tables, create indexes,
reset sequences, downcase identifiers
SET PostgreSQL PARAMETERS
maintenance_work_mem to '512MB'
CAST type datetime to timestamptz drop default drop not null using zero-dates-to-null,
type year to integer drop typemod
EXCLUDE TABLE NAMES LIKE 'temp_%', 'log_%'
BEFORE LOAD DO
$$ CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; $$
AFTER LOAD DO
$$ ANALYZE; $$;
Oracle → PostgreSQL (pgloader)
# pgloader 也支持 Oracle(需要 Oracle Instant Client)
pgloader oracle://user:pass@oracle-host:1521/ORCL \
postgresql://user:pass@pg-host/mydb
19.2.1 ora2pg(Oracle 专用迁移工具)
ora2pg 是 Oracle 到 PostgreSQL 最专业的迁移工具,支持 Schema 转换、数据迁移和代码翻译。
# 安装
sudo apt install ora2pg
# 初始化配置模板
ora2pg --project_base /opt/migration --init_project myproject
# 编辑配置 /opt/migration/myproject/ora2pg.conf
# ORACLE_DSN dbi:Oracle:host=oracle-host;sid=ORCL;port=1521
# ORACLE_USER system
# ORACLE_PWD manager
# PG_DSN dbi:Pg:dbname=mydb;host=pg-host;port=5432
# PG_USER postgres
# PG_PWD password
# 只迁移 Schema
ora2pg -c ora2pg.conf --type TABLE --dump_schema > schema.sql
# 迁移数据
ora2pg -c ora2pg.conf --type COPY --dump_data > data.sql
# 迁移 PL/SQL → PL/pgSQL
ora2pg -c ora2pg.conf --type FUNCTION --dump_as_html > functions.html
# 生成迁移报告
ora2pg -c ora2pg.conf --estimate_cost
Oracle → PG 类型映射
| Oracle 类型 | PostgreSQL 类型 | 说明 |
|---|
NUMBER(1) | BOOLEAN | ora2pg 自动转换 |
NUMBER(n) | NUMERIC(n) | — |
NUMBER(n,0) | BIGINT 或 INTEGER | 视精度选择 |
VARCHAR2(n) | VARCHAR(n) 或 TEXT | 建议用 TEXT |
CLOB | TEXT | — |
BLOB | BYTEA | — |
DATE | TIMESTAMPTZ | Oracle DATE 包含时间 |
TIMESTAMP WITH LOCAL TIME ZONE | TIMESTAMPTZ | — |
ROWID | ctid 或自增主键 | 需要改造 |
SEQUENCE.NEXTVAL | nextval('seq') 或 IDENTITY | — |
⚠️ 注意事项:Oracle 的 DATE 类型包含时间部分,对应 PostgreSQL 的 TIMESTAMP 或 TIMESTAMPTZ,而非 DATE。这是最常见的迁移陷阱。
19.3 其他迁移工具
| 工具 | 来源 | 目标 | 特点 |
|---|
| pgloader | MySQL/MS SQL/SQLite/CSV | PG | 最通用 |
| ora2pg | Oracle | PG | Oracle 专用,最成熟 |
| pg_chameleon | MySQL | PG | 实时复制 |
| AWS DMS | 多种 | PG(RDS/Aurora) | 云环境 |
| pg_dump/pg_restore | PG | PG | 同版本迁移 |
19.4 CSV 批量导入
-- COPY 命令(最快)
COPY employees (name, email, age, salary)
FROM '/tmp/employees.csv'
WITH (FORMAT csv, HEADER true, DELIMITER ',', NULL '');
-- 从 STDIN(编程方式)
\COPY employees (name, email) FROM STDIN WITH CSV HEADER
-- 使用 fdw 导入
CREATE FOREIGN TABLE csv_import (
name TEXT, email TEXT
) SERVER csv_server
OPTIONS (filename '/tmp/data.csv', format 'csv', header 'true');
INSERT INTO employees (name, email)
SELECT name, email FROM csv_import;
批量导出
-- 导出为 CSV
COPY (SELECT * FROM employees WHERE department = '工程部')
TO '/tmp/engineering.csv' WITH (FORMAT csv, HEADER true);
-- \COPY(客户端导出)
\COPY employees TO '/tmp/employees.csv' WITH CSV HEADER
19.5 数据一致性验证
-- 行数对比(源库和目标库分别执行)
SELECT table_name, row_count
FROM (
SELECT 'employees' AS table_name, count(*) AS row_count FROM employees
UNION ALL
SELECT 'orders', count(*) FROM orders
UNION ALL
SELECT 'products', count(*) FROM products
) sub
ORDER BY table_name;
-- 校验和验证(对比特定列)
SELECT
count(*) AS total_rows,\ sum(hashtext(row::text)) AS checksum
FROM employees;
-- 使用 pg_comparator 扩展进行跨库对比
-- 或手动编写脚本:
-- 1. 源库 count(*) 导出
-- 2. 目标库 count(*) 对比
-- 3. 抽样对比关键字段
MySQL → PG 类型映射
| MySQL 类型 | PostgreSQL 类型 | 注意事项 |
|---|
TINYINT(1) | BOOLEAN | pgloader 自动转换 |
INT / BIGINT | INTEGER / BIGINT | — |
DOUBLE | DOUBLE PRECISION | — |
VARCHAR(n) | VARCHAR(n) 或 TEXT | 建议 TEXT |
TEXT / MEDIUMTEXT / LONGTEXT | TEXT | — |
DATETIME | TIMESTAMPTZ | 注意时区 |
TIMESTAMP | TIMESTAMPTZ | PG 的 TIMESTAMP 不含时区 |
ENUM('a','b') | TEXT + CHECK 或自定义 ENUM | PG 的 ENUM 修改不如 MySQL 灵活 |
JSON | JSONB | PG 的 JSONB 更强大 |
BLOB / LONGBLOB | BYTEA | — |
AUTO_INCREMENT | GENERATED ALWAYS AS IDENTITY | — |
19.6 迁移检查清单
| 步骤 | 说明 |
|---|
| ☐ 数据库 schema 对比 | 类型映射、约束差异 |
| ☐ 索引迁移 | 确保索引被正确创建 |
| ☐ 数据一致性验证 | 行数对比、校验和 |
| ☐ 性能基准测试 | 迁移后执行 EXPLAIN ANALYZE |
| ☐ 应用兼容性测试 | SQL 语法差异、驱动兼容 |
| ☐ 回退方案 | 确保能回退到旧系统 |
| ☐ 监控告警 | 迁移后密切关注性能指标 |
业务场景
| 场景 | 推荐方案 |
|---|
| PG 版本升级 | pg_upgrade |
| MySQL → PG | pgloader |
| Oracle → PG | ora2pg |
| 小量数据 (< 1GB) | pg_dump/pg_restore |
| 实时迁移 | 逻辑复制 |
扩展阅读