PostgreSQL数据库迁移实践:场景分类、方案对比与数据校验指南
大家好,我是数据库小学妹 👋
上周有个朋友找我帮忙。他们公司要把跑了三年的PostgreSQL数据库从本地机房迁到云上,200G数据,业务不能停。他先用了pg_dump导出,跑了一晚上没跑完,第二天业务直接卡死。后来改方案又踩了角色权限的坑,序列值对不上,前后折腾了快两周。
他问我,PostgreSQL迁移到底应该按什么思路来?
我想了很久。因为PostgreSQL迁移不是一个动作,而是一套决策。什么场景、选什么方案、用什么工具、迁移完怎么验证。每个环节都有坑,每一步都不能省。
PostgreSQL迁移,指的是将PostgreSQL数据库从一个运行环境完整转移到另一个环境的过程,包括结构迁移、数据迁移、权限迁移和迁移后验证四个阶段。迁移方式可以按数据量、停机窗口和目标环境分为多种方案。
今天把常见的迁移场景梳理一遍。从最简单的跨服务器搬数据,到不停机迁移,再到异构数据库替换。每个场景对应什么方案、踩什么坑、怎么验证,都写清楚。
PostgreSQL迁移前检查清单
动手之前,先过一遍清单。这些是迁移时最容易忽略的前置动作。
确认版本兼容。大版本不同(比如11到15),pg_dump导出的SQL可能包含目标库不支持的语法。先在目标库跑一遍\l确认版本,小版本差异一般没问题。
评估数据量和停机窗口。200G以上的库,单线程pg_dump导出可能要跑大半天。数据量直接决定能不能用pg_dump方案,停机窗口决定业务能接受多长的中断时间。
检查网络连通性。源库到目标库之间的延迟直接影响迁移速度。云环境下提前配置好安全组、VPC和防火墙规则,别等导出跑完了才发现目标库连不上。
提前做好全量备份。不管选哪种方案,迁移前先用pg_dump做一份完整备份放到独立存储。我有一次做迁移,源库数据直接搞坏了,幸亏提前留了备份。
列好extension清单。PostGIS、pgcrypto、uuid-ossp这些扩展插件不会自动迁移。迁移前在源库跑一遍\dx,把结果截个图或记下来,目标库初始化时就装上,免得导入时报"extension does not exist"。如果是迁移到金仓KES这类国产数据库,兼容性评估阶段就要把extension的替代方案一起确认,KES对大部分常用扩展都有对应的兼容实现。
一、PostgreSQL迁移的五种场景
很多人一上来就选工具,这个顺序其实搞反了。要先搞清楚你要解决的是哪种迁移场景,这样工具选型自然就出来了。
1.1 跨服务器迁移
最常见的场景。旧服务器退役,数据搬到新机器。或者开发环境的数据同步到测试环境。
同源同版本,目标端是全新的PostgreSQL实例。数据量从几GB到几百GB都有。
1.2 上云迁移
本地PostgreSQL迁到云数据库,比如阿里云RDS、腾讯云TDSQL。
目标端是托管服务,没有superuser权限。角色权限模型和自建库不同,这是和跨服务器迁移最大的区别。
1.3 版本升级
从PostgreSQL 11升到15,或者从14升到17。
同服务器、同实例,只是版本变了。但大版本升级涉及系统目录结构变化,不能简单复制data目录。
1.4 跨云迁移
从一个云厂商迁到另一个。比如阿里云RDS迁到腾讯云,或者AWS RDS迁回本地。
两端都是托管环境,网络延迟大,且双方权限都受限。
1.5 异构迁移
从PostgreSQL迁到另一个数据库引擎。比如信创场景下迁到国产数据库。
SQL语法、数据类型、存储过程语言可能完全不同。异构迁移最难的部分不是数据搬运,而是兼容性评估和语法改造。
二、主流PostgreSQL迁移方案对比
场景明确了,再看方案。我把主流方案归为四类:
| 方案 | 适用场景 | 停机时间 | 数据量上限 | 复杂度 |
|---|---|---|---|---|
| pg_dump + pg_restore | 跨服务器、小数据量上云 | 高(数小时) | <100GB | 低 |
| 物理复制(pg_basebackup) | 大版本内迁移、灾备 | 中(分钟级) | 无上限 | 中 |
| 逻辑复制(Logical Replication) | 不停机迁移、跨云 | 极低(秒级切换) | 无上限 | 高 |
| 专业迁移工具(KDTS等) | 异构迁移、大规模上云 | 可做到不停机 | 无上限 | 中 |
| pg_upgrade | 同机大版本升级 | 低(分钟级) | 无上限 | 低 |
下面逐个拆解。
2.1 pg_dump + pg_restore:常用,也常踩坑
绝大多数人的第一选择。pg_dump是PostgreSQL自带的,不用装额外工具。
标准操作流程:
源端导出:
pg_dump -h 源库IP -p 5432 -U postgres -d mydb -F c -f /tmp/mydb.dump
目标端导入:
pg_restore -h 目标库IP -p 5432 -U postgres -d mydb -c /tmp/mydb.dump
-F c表示custom格式,pg_restore能读取。-c会在导入前先drop已有对象,避免冲突。
但有几个坑比较常见:
pg_dump不导出全局对象。角色、用户、表空间这些是全局的,需要额外用pg_dumpall -g导出。
上云场景没有superuser。阿里云RDS、腾讯云TDSQL这些托管服务,master用户不是superuser。很多在自建库能正常执行的pg_dump命令,到云上就报权限错误。这时候需要修改导出的SQL,把OWNER TO postgres改成OWNER TO master用户。
大数据量导出慢。pg_dump是单线程的,100GB以上的库可能跑好几个小时。可以用-j参数做并行导出,但-j和--single-transaction不能同时用。
2.2 pg_basebackup:物理级复制,适合大数据量
数据量超过几百GB时,pg_dump不太合适了。
pg_basebackup做的是物理级别的备份,直接把PostgreSQL的data目录完整复制过来。速度快,但要求源和目标大版本一致。
pg_basebackup -h 源库IP -D /var/lib/pgsql/data -U replicator -P -v -R
-R会自动生成standby.signal和连接配置,方便后续启动为standby或promote为主库。
适用于同大版本的PostgreSQL迁移,数据量大,能接受分钟级停机窗口的场景。
迁移前要停写,确保数据一致。迁移后需要修改postgresql.conf和pg_hba.conf中的IP配置,否则启动会失败。
2.3 逻辑复制:不停机PostgreSQL迁移的手段
业务24小时不能停的话,逻辑复制是目前比较成熟的方案。
原理是在源库配置发布(publication),目标库配置订阅(subscription)。全量数据先同步一遍,之后源库的增量变更通过WAL日志实时推到目标库。两边数据保持同步,业务可以在任意时间窗口做最终切换。
源库配置发布:
CREATE PUBLICATION my_migration_pub FOR ALL TABLES;
目标库配置订阅:
CREATE SUBSCRIPTION my_migration_sub
CONNECTION 'host=源库IP dbname=mydb user=replicator password=xxx'
PUBLICATION my_migration_pub;
切换时只需要在目标库停掉订阅,把应用连接指向新库,整个过程通常几秒钟。
逻辑复制有几个前提条件:
- 源库需要设置
wal_level = logical - 每张需要复制的表必须有主键或REPLICA IDENTITY
- 序列值不会自动同步,切换前需要手动修正
- DDL变更(ALTER TABLE等)不会自动复制
2.4 专业迁移工具:异构场景和大规模上云
PostgreSQL迁移涉及异构数据库时,比如迁到国产数据库金仓KES,纯手工方式效率太低。专业迁移工具更靠谱。
以金仓数据迁移工具 Kingbase Data Migration Tool(KDTS)为例,流程大致是:
- 兼容性评估:用KDMS扫描源PostgreSQL库,自动生成兼容性报告,标记哪些对象可以直接迁移、哪些需要改造
- 结构迁移:自动转换表结构、索引、约束、序列等DDL,处理数据类型映射
- 全量迁移:并行导出数据并导入目标库,支持断点续传
- 增量同步:基于日志捕获变更数据,实时同步到目标端
异构迁移最耗时的是存储过程改造。PL/pgSQL到PL/SQL的语法差异比较大,函数签名、异常处理、游标写法都要调整。KDMS的评估报告会把这些问题标出来,能自动修的直接修,修不了的再交给DBA手工改。
某制造企业源库有数百张表和大量存储过程。KDMS评估后多数兼容性问题可以自动修复,剩下主要是PL/pgSQL特有语法和少量数组类型映射,人工改造几周就搞定了。
2.5 pg_upgrade:同机大版本升级的快捷方式
版本升级场景下(比如PostgreSQL 11升到15),如果新旧版本装在同一台机器上,pg_upgrade是比较高效的方案。
它直接读取旧版本的数据目录,在新版本的数据目录中重建系统目录,不需要导出再导入SQL。速度比pg_dump快很多,因为数据文件本身不移动,只是系统目录结构被更新。
两种模式:
--link模式:用硬链接代替复制,几乎不需要额外磁盘空间,速度极快。但升级完成后旧版本就不能再启动了,因为数据文件已经被新版本链接走。
--copy模式:完整复制数据文件,需要双倍的磁盘空间,但升级完成后旧版本仍然可用,出问题可以回退。
pg_upgrade -b /usr/lib/postgresql/11/bin -B /usr/lib/postgresql/15/bin \
-d /var/lib/postgresql/11/main -D /var/lib/postgresql/15/main \
--link --check
--check参数只做预检查不实际升级,建议先跑一遍check确认没有兼容性问题再执行。
pg_upgrade完成后还需要运行analyze_new_cluster.sh脚本更新统计信息,否则查询计划器会使用过时的统计数据,查询性能可能暂时下降。
三、PostgreSQL迁移后的数据一致性校验
这一步,绝大多数教程都不写。我吃过亏,所以必须写。迁移完成不等于迁移成功。数据有没有丢?序列值对不对?索引有没有漏建?都需要验证。
3.1 对象级校验
对比源库和目标库的数据库对象数量:
-- 对比表数量
SELECT count(*) FROM information_schema.tables
WHERE table_schema = 'public' AND table_type = 'BASE TABLE';
-- 对比索引数量
SELECT count(*) FROM pg_indexes WHERE schemaname = 'public';
-- 对比序列值
SELECT last_value FROM my_sequence;
序列值容易忽略。PostgreSQL迁移后,序列值可能停在旧位置。业务继续写入就会报主键冲突。切换前一定要检查所有序列,确保last_value不小于源库。
3.2 数据级校验
抽样核对核心表的数据量:
SELECT tablename, n_live_tup
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC;
关键业务表做行数对比和内容抽查。条件允许的话,用数据对比工具逐行校验。金仓KDC提供了结构对比和数据对比能力,能自动识别差异对象并生成订正SQL。
3.3 功能级校验
跑一遍核心业务的回归测试。用户登录、订单创建、报表查询,这些高频操作在目标库上能不能正常跑通,是最终的验证标准。
四、PostgreSQL迁移决策框架
面对一个具体的PostgreSQL迁移任务,按这个流程走:
| 步骤 | 判断维度 | 决策点 | 推荐方案 | 注意事项 |
|---|---|---|---|---|
| 第一步:明确场景 | 迁移场景 | 同版本跨服务器 | pg_dump/pg_restore | 数据量<100GB够用 |
| 上云托管 | pg_dump+角色预创建 | 目标端无superuser,需改OWNER | ||
| 大版本升级 | pg_upgrade或逻辑复制 | 不可直接复制data目录 | ||
| 跨云迁移 | 逻辑复制或专业工具 | 两端都受限,网络延迟大 | ||
| 异构迁移 | 先做兼容性评估 | SQL语法/数据类型需改造 | ||
| 第二步:看数据量 | 数据量级 | <100GB | pg_dump | 单线程,预估导出时间 |
| 100GB~1TB | pg_dump并行或pg_basebackup | -j和--single-transaction互斥 |
||
| >1TB | 物理复制或逻辑复制 | 需提前规划停机窗口 | ||
| 第三步:看停机窗口 | 可接受停机时间 | 能接受数小时 | pg_dump/pg_restore | 最简单,但停机时间最长 |
| 分钟级 | pg_basebackup或逻辑复制 | 切换前需同步序列值 | ||
| 要求不停机 | 逻辑复制或专业工具 | 需配置WAL和publication | ||
| 第四步:迁移后验证 | 验证方式 | 对象对比 | 表/索引/序列数量对比 | 用information_schema和pg_indexes |
| 数据校验 | 核心表行数对比 | 条件允许做逐行校验 | ||
| 业务回归 | 高频操作跑一遍 | 登录/订单创建/报表查询 |
五、PostgreSQL迁移常见错误与排查
这一步很多讲PostgreSQL迁移的文章都会讲。我把反复遇到的报错整理出来,附排查命令和修复方法。
5.1 权限报错:permission denied for extension
上云迁移最常见。源库有superuser,目标云数据库(阿里云RDS、腾讯云TDSQL、AWS RDS)的master用户不是superuser。pg_dump导出的SQL里包含CREATE EXTENSION plpgsql之类的语句,到云上执行直接报错。
修复方法:打开导出的SQL文件,找到CREATE EXTENSION相关语句,确认目标库是否已预装该扩展。用\dx查看目标库已安装的扩展列表。plpgsql在云数据库通常已预装,直接删掉这条语句即可。
-- 查看目标库已安装的扩展
\dx
5.2 角色不存在:role “postgres” does not exist
pg_dump不导出全局角色,但导出的SQL里包含OWNER TO postgres这样的语句。导入目标库时如果postgres用户不存在,每条ALTER TABLE都会报错。
修复方法有两种。第一种是迁移前用pg_dumpall -g单独导出全局角色,先在目标库创建好用户再导入数据。第二种是批量替换SQL文件中的OWNER语句:
# 把OWNER TO postgres替换为目标库的master用户
sed -i 's/OWNER TO postgres/OWNER TO master_user/g' dump.sql
云环境下还要注意,某些角色属性(SUPERUSER、BYPASSRLS)在目标库不被允许。从role.sql中去掉这些属性再执行。如果是迁到金仓KES这类对PostgreSQL生态兼容度较高的数据库,角色和权限模型基本保持一致,pg_dump导出的SQL可以直接使用,不需要做OWNER替换,这比迁到云托管服务要省事不少。
5.3 序列值不同步:主键冲突
逻辑复制不复制序列值,pg_dump用默认参数导出时,序列值可能不是最新的。迁移完成后业务继续写入,新插入数据的id和已有数据冲突。
排查方法:在源库和目标库分别执行:
-- 查看序列当前值
SELECT last_value FROM my_table_id_seq;
-- 对比源库和目标库的结果,取较大值修正
SELECT setval('my_table_id_seq', 源库last_value);
如果表比较多,可以用这条SQL一次性查出所有序列的当前值:
SELECT c.relname AS sequence_name, last_value
FROM pg_sequences s
JOIN pg_class c ON c.relname = s.sequencename;
5.4 pg_restore导入中断后无法继续
用了-j并行参数就不能加--single-transaction,一旦中途报错,已导入的部分不会回滚。这时候直接重新跑会报"关系已存在"。
修复方法:清空目标库后重新导入,或者用pg_restore --list查看TOC列表,跳过已导入的部分继续。建议导入前先用\l确认目标库是干净的,或者用-c参数在导入前先drop已有对象。
六、PostgreSQL迁移避坑清单
反复踩的坑,总结成几条。具体报错排查见上一节,这里只写注意事项。
全量备份一定放在独立存储。我有一次备份文件和源库放在同一台机器上,迁移过程中磁盘故障,备份跟着一起丢了。备份要放到另一台机器或对象存储上。
pg_basebackup迁移前必须停写。物理复制要求数据一致,迁移开始前先停掉应用的写操作,确认没有活跃事务再执行,否则复制过来的data目录启动后可能出现数据不一致。
pg_upgrade升级后别急着上线。升级完成后要跑analyze_new_cluster.sh更新统计信息,不然查询计划器会用旧统计数据,慢查询会突然变多。等analyze跑完、核心业务测试通过了再切换流量。
异构迁移先跑兼容性评估再动手。不要一上来就导数据。用工具扫一遍源库,看清楚哪些能自动迁移、哪些要手工改,心里有数再执行,能省大量返工时间。
小结
PostgreSQL迁移不是一个工具能解决的。场景不同、方案不同、坑也不同。跨服务器小数据量用pg_dump比较省心,大数据量上物理复制或逻辑复制,同机版本升级用pg_upgrade效率高,异构迁移先做评估再动手。迁移完一定要做验证,对象对比、数据校验、业务回归,三步缺一不可。如果是信创场景下的异构迁移,金仓KES对PostgreSQL的兼容度高,迁移工具和兼容性评估也比较成熟,能减少不少改造工作量。
大家在做PostgreSQL迁移时遇到过什么坑?或者有什么好的迁移经验?评论区聊聊 👇
我是数据库小学妹,咱们下篇见 👋
- 点赞
- 收藏
- 关注作者
评论(0)