前两天和一个做 DBA 的朋友聊天,他正为一批 PostgreSQL 数据的迁移头疼不已。几十个 TB 的数据要从旧集群搬到新集群,业务又不能停太久,迁移过程中还必须保证数据一致性。他尝试了好几种方案,要么速度太慢,要么数据对不上,折腾了快两周仍未解决。这让我想起自己第一次接触 PostgreSQL 数据迁移时的狼狈——那时真的以为只要把数据导出来再导进去就行,结果各种坑踩了个遍。

PostgreSQL 数据迁移表面上是技术活,实际上考验的是对整个生态的理解。很多人一上来就想着用 pgdump 导出,再用 psql 导入,这套流程在小规模数据下确实够用,但一旦数据量上升到几百 GB 甚至 TB 级,问题就会全部暴露。pgdump 是逻辑备份工具,会把数据转换成 SQL 语句再执行,这个过程不仅慢,而且对系统资源的消耗特别大。我见过一个案例,有人用 pgdump 迁移 2 TB 的数据,跑了整整三天仍未完成,因为临时表空间撑爆直接报错,前功尽弃。
再说说物理迁移方案,比如直接拷贝数据目录。这种方式速度快,但坑更多。PostgreSQL 的数据目录结构复杂,不同版本之间的格式可能不兼容,跨大版本迁移时尤其容易出问题。更麻烦的是,如果源库和目标库的操作系统架构不一样,比如一个是 x86、一个是 ARM,拷贝过去的数据根本无法使用。还有一个致命问题——拷贝数据时数据库必须停机,或者至少保证数据是静止状态。这对生产环境几乎不可能,业务不能断,数据还在不断写入,怎么拷贝?
那在线迁移呢?逻辑复制或者流复制?逻辑复制可以实现不停机迁移,但它的原理是基于 WAL 日志解析,把变更记录转换成逻辑格式再应用到目标库。这个过程有个天然短板——初始同步阶段会把全量数据先搬过去,如果数据量太大,同样会很慢。而且逻辑复制对表结构有要求,主键是必须的,没有主键的表会被跳过或报错。更头疼的是,DDL 操作不支持复制,源库新建表或修改字段类型时,逻辑复制不会自动同步,需要手动处理。
流复制支持物理级别的同步,延迟低、性能好,但它要求主库和备库的 PostgreSQL 版本、操作系统、甚至编译参数都必须完全一致。这在同机房同版本升级的场景下很好用,但如果是跨版本、跨平台或跨云环境,流复制就难以实现。我见过一个项目,从自建 PostgreSQL 迁移到云上的 RDS,想用流复制做实时同步,结果发现云厂商的 RDS 限制了超级用户权限,流复制需要的 replication slot 根本创建不了,只能另想办法。
说回工具选择,pgdump 和 pgrestore 是最基础的,适合小规模或开发测试环境。生产环境推荐使用 pgbasebackup 做基础备份,再配合 WAL 日志做增量恢复。这种方式比逻辑备份快得多,而且可以做到不停机。具体做法是先在目标机用 pgbasebackup 拉一份全量数据,然后持续从源库拉取 WAL 日志,等数据追平后再切换业务。听起来简单,实际操作时需要注意几个细节:WAL 日志的保留策略、归档目录的配置、网络带宽的保障,任何一个环节出问题都可能导致数据不一致。
还有一个被很多人忽略的因素——字符集和编码问题。PostgreSQL 默认编码是 UTF8,但很多老系统使用 GBK 或 Latin1。迁移时如果源库和目标库的编码不一致,数据导进去后中文会变成乱码,而且这种问题往往在迁移完成后很久才被发现。我遇到过一个案例,某电商系统从 SQLite 迁移到 PostgreSQL,源库用的是 GBK 编码,迁移时没注意编码转换,结果所有商品名称全部乱码,修复数据花了整整两天。所以迁移前一定要确认字符集,不一致的话必须提前规划好转换方案。
迁移完成后的验证环节同样不能马虎。很多人以为数据导进去就完事了,结果业务跑起来各种报错,要么索引缺失,要么序列号不对。正确的做法是迁移完成后立即执行数据一致性校验,比如对比源库和目标库的表记录数、校验关键字段的哈希值、检查主外键约束是否完整。如果有条件,最好在迁移前就写一套自动化校验脚本,迁移完成后一键执行,把手工检查变成自动化流程。
还有一个细节容易被忽略——序列的当前值。PostgreSQL 的序列和表的自增字段是分开管理的,迁移后序列的起始值可能和源库不一致。比如源库的订单表 ID 已经到了 100,迁移后目标库的序列可能从 1 开始重新计数,这时新插入的数据就会和旧数据冲突。解决方法是迁移完成后手动设置序列的当前值,或者用 setval 函数同步源库的序列值。这个操作虽然简单,但如果忘了处理,后果会很严重。
回到开头那个朋友的案例,他选择了 pgbasebackup 加 WAL 持续同步的方案。把源库的 WAL 日志归档到 NFS 共享存储,目标库持续从 NFS 拉取并应用 WAL。等数据追平后,在业务低峰期切换 DNS 指向,整个过程业务中断时间控制在 5 分钟以内。迁移完成后他跑了三天的数据一致性校验,对比了几百张表的记录数和哈希值,全部匹配。他后来跟我说,这次迁移最大的收获不是技术本身,而是对 PostgreSQL 生态的敬畏——每个工具都有适用场景,选错了就是给自己挖坑。
说到底,PostgreSQL 数据迁移不是一锤子买卖,而是一个系统工程。从方案选型到环境准备,从执行迁移到事后验证,每个环节都必须提前想清楚。特别是生产环境,宁可多花时间做测试和演练,也别抱着“先跑了再说”的心态。数据是企业的命根子,迁移出问题,轻则影响业务,重则数据丢失,这个代价谁都承担不起。与其事后补救,不如事前把功课做好——这大概是所有 DBA 和运维人员在数据迁移这件事上最大的共识。


