一、数据迁移的应用场景

在我参与的某电商平台架构升级项目中,需将运行在CentOS 7的PostgreSQL 9.6集群整体迁移至Ubuntu 22.04上的PostgreSQL 14.9版本。这个典型场景涉及跨操作系统版本(CentOS→Ubuntu)、数据库版本跨度大(9.6→14.9)、数据规模超5TB的三重挑战,考验着数据迁移策略的设计能力。

同类场景还包括:

  • 本地机房迁移至公有云(如AWS RDS)
  • 从x86架构服务器迁移到ARM架构服务器
  • 海外业务合规要求的区域化数据迁移

二、技术方案选择与优缺点对比

2.1 物理复制方案

使用工具:pg_basebackup + pg_upgrade

优点

  • 秒级停机切换
  • 完美保留表结构、索引状态

缺点

  • 必须保持主版本号一致(如不能直接从9.6升级到14)
  • 跨平台可能产生二进制不兼容
# 适用场景:小版本升级(例如13.3→13.8)
pg_upgrade \
  -b /old/postgres/bin \
  -B /new/postgres/bin \
  -d /old/data \
  -D /new/data \
  --link

2.2 逻辑导出方案

使用工具:pg_dump + pg_restore

优点

  • 天然解决跨架构迁移问题
  • 可选择性迁移特定schema

缺点

  • 大表导出时容易OOM
  • 事务完整性依赖导出顺序
# 并行导出示例(使用24个线程)
pg_dump -Fd -j 24 -f /data/dump_dir mydb

# 并行恢复(需关闭触发器)
pg_restore -j 16 --disable-triggers -d newdb /data/dump_dir

2.3 逻辑复制方案

使用工具:PUBLICATION/SUBSCRIPTION

优点

  • 支持增量同步
  • 最小化停机时间

缺点

  • 要求主备库均使用10+版本
  • 需要处理DDL变更
-- 在源库创建发布
CREATE PUBLICATION migration_pub FOR ALL TABLES;

-- 在目标库创建订阅
CREATE SUBSCRIPTION migration_sub
  CONNECTION 'host=source_host dbname=mydb'
  PUBLICATION migration_pub
  WITH (copy_data = true);

三、跨平台迁移的关键技术点

3.1 文件编码与排序规则

Linux与Windows的文件路径大小写敏感差异可能导致SQL脚本执行失败:

-- 错误示例(Linux系统区分大小写)
CREATE TABLE "OrderHistory" (...);  -- 双引号强制保留大小写

-- 正确写法(统一使用小写命名)
CREATE TABLE order_history (...);

3.2 特殊类型兼容处理

迁移jsonb字段时注意版本差异:

-- PostgreSQL 9.6不支持jsonb_path_exists
SELECT data->'user'->>'name' 
FROM logs 
WHERE data @> '{"status": "success"}';

-- PostgreSQL 14支持路径查询
SELECT jsonb_path_query(data, '$.user.name')
FROM logs 
WHERE jsonb_path_exists(data, '$.status ? (@ == "success")');

3.3 扩展模块的移植方案

处理不同平台的PostGIS扩展:

# 在目标集群预装相同版本的PostGIS
sudo apt install postgresql-14-postgis-3

# 使用pg_dump单独导出扩展
pg_dump -Fc -n public -T spatial_ref_sys mydb > custom.dump

四、实战案例:跨境数据迁移完整流程

4.1 迁移前检查清单

  • 确认目标库字符集为UTF8:
SHOW server_encoding;
  • 核对时区配置:
SELECT current_setting('TIMEZONE');

4.2 混合迁移策略实施

结合物理复制和逻辑导出的混合方案:

# 第一步:全量物理复制
pg_basebackup -h source_host -D /var/lib/pgsql/14/data -Xs -P

# 第二步:增量差异同步
psql -c "SELECT pg_create_restore_point('before_final_switch')"
rsync -avz --delete source_host:/pg_wal/ /pg_wal_archive/

4.3 验证数据一致性的Python脚本

# compare_md5.py (Python 3.8+)
import psycopg2

def get_table_md5(conn, table):
    cur = conn.cursor()
    cur.execute(f"SELECT md5(array_agg(t)::text) FROM {table} t")
    return cur.fetchone()[0]

source_conn = psycopg2.connect("host=src_host dbname=mydb")
dest_conn = psycopg2.connect("host=dest_host dbname=mydb")

tables = ['users', 'orders', 'products']
for tbl in tables:
    src_md5 = get_table_md5(source_conn, tbl)
    dst_md5 = get_table_md5(dest_conn, tbl)
    assert src_md5 == dst_md5, f"{tbl} 数据不一致"

五、必须避开的陷阱

5.1 序列值不同步

迁移后务必同步序列:

-- 获取当前序列值
SELECT last_value FROM user_id_seq;

-- 重设序列起始值
ALTER SEQUENCE user_id_seq RESTART WITH 1000000;

5.2 忽略物化视图依赖

处理物化视图的正确顺序:

-- 错误的直接刷新
REFRESH MATERIALIZED VIEW sales_summary;  -- 可能缺少基础表

-- 正确的顺序化刷新
BEGIN;
  REFRESH MATERIALIZED VIEW product_stats;
  REFRESH MATERIALIZED VIEW sales_summary;
COMMIT;

六、迁移后的性能调优技巧

6.1 统计信息更新

强制刷新全库统计信息:

vacuumdb --analyze-in-stages -j 8 mydb

6.2 索引重建策略

避免直接REINDEX导致锁表:

-- 使用CONCURRENTLY选项
REINDEX INDEX CONCURRENTLY idx_order_date;

七、特别注意事项手册

7.1 外键约束的处理

迁移期间禁用外键检查:

SET session_replication_role = replica;  -- 禁用触发器与外键
-- 执行数据导入操作
SET session_replication_role DEFAULT;

7.2 大对象迁移方案

单独处理超过2GB的PDF文件:

pg_dump -Fc -t large_objects mydb > lobs.dump
pg_restore -d newdb lobs.dump

八、技术演进趋势观察

云原生时代的数据迁移呈现以下变化:

  • 物理卷快照技术的应用(如AWS EBS快照)
  • 逻辑复制支持双向同步
  • PG15新增的mergetsdb扩展支持多主架构