导读:本期聚焦于大卫创作的《如何用pg_upgrade --link硬链接模式快速升级PostgreSQL?》,敬请观看详情。数据目录几百GB的PostgreSQL要做大版本升级,常规复制方式耗时和磁盘占用都令人头疼。pg_upgrade的--link模式通过硬链接直接关联旧集群与新集群的数据文件,能将升级时间从小时级压缩到分钟级。本文介绍这一模式的工作机制、前置条件、完整执行步骤与回滚策略,并解释为什么硬链接模式虽然快但不能在升级后继续启动旧集群。文章还会给出检查命令和常见报错处理思路,帮助你在保障数据安全的前提下完成快速升级。

PostgreSQL大版本升级通常需要处理数据目录中的大量文件,常规的复制模式会占用额外磁盘空间,并且数据量越大耗时越长。pg_upgrade工具提供了--link选项,利用文件系统硬链接直接关联旧集群与新集群的数据文件,从而跳过复制过程。这种方式在数据量达到数百GB时优势尤其明显,几分钟内即可完成核心升级操作。本文详细说明该模式的工作原理、使用条件、完整升级流程以及风险控制方法。

如何用pg_upgrade --link硬链接模式快速升级PostgreSQL?

一、硬链接模式到底做了什么

普通文件系统中的文件由inode和目录项共同描述。一个文件的数据是否被复制,取决于是否创建了新的inode以及新的数据块。硬链接则是在同一种文件系统内创建另一个目录项,让它指向同一个inode,因此不会产生第二份数据副本。pg_upgrade --link正是利用这一特性,把旧版本数据目录里的表数据文件、索引文件等通过硬链接关联到新版本数据目录中。

这样处理后,旧集群和新集群在物理上共享相同的数据块,升级过程中不需要逐个复制文件,既显著降低时间成本,也几乎不增加磁盘占用。可以通过ls -li查看两个文件的inode号来验证硬链接关系。以下命令会在同一个文件系统内创建一个硬链接,并展示两个目录项拥有相同的inode号。

# 创建测试文件
echo hello > /tmp/test_file

# 创建硬链接
ln /tmp/test_file /tmp/test_file_link

# 查看inode号
ls -li /tmp/test_file /tmp/test_file_link

硬链接模式的关键限制也由此产生:硬链接不能跨文件系统创建。如果旧数据目录和新数据目录位于不同的挂载点,或者某些表空间指向了不同的文件系统,pg_upgrade --link就会失败。因此使用前必须确认相关目录都在同一文件系统内。

还需要注意,硬链接并非快照。当新集群启动并修改共享的数据页时,旧集群看到的内容也会发生变化。官方文档明确说明,使用链接模式升级后,旧集群不再能够被安全地启动。升级前必须做好完整备份,不能寄希望于保留旧集群作为回滚手段。

二、升级前的准备与兼容性检查

首先应确认当前PostgreSQL版本和目标版本都受pg_upgrade支持。较新的pg_upgrade可以处理多个旧版本,但过于古老的版本可能需要先做中间升级。实际生产环境一般会从较新的稳定版本直接升级到更高稳定版本,例如从PostgreSQL 14升级到16。升级前需要安装好目标版本的二进制程序,并确认pg_config或bin目录位置。

磁盘空间虽然不再是主要瓶颈,但初始化新集群、生成临时文件以及日志文件仍会消耗少量空间。更重要的是需要检查旧数据目录、新数据目录以及所有表空间目录是否位于同一文件系统。可以使用df -T查看挂载点和文件系统类型,确保它们一致。

在正式升级前,强烈建议先运行一次完整备份。pg_upgrade不会自动备份数据,一旦升级过程中发生不可恢复的错误,备份就是最后的防线。同时应安装新版本对应的扩展包,如果旧集群使用了PostGIS、pg_stat_statements等扩展,新版本环境中也必须提供兼容版本,否则升级后的数据库可能无法正常加载这些扩展。

以下命令可用来确认旧集群状态以及目录文件系统信息。

# 查看旧集群数据目录所在文件系统
df -T /pgdata/old

# 查看新集群数据目录所在文件系统
df -T /pgdata/new

# 确认旧集群版本
/usr/pgsql-14/bin/psql -c "SELECT version();"

三、完整升级步骤

第一步是停止旧版本实例。建议使用快速模式停止,确保所有连接断开并且数据已经落盘。例如执行pg_ctl stop -D /pgdata/old -m fast。停库后不要再次启动旧集群,否则可能破坏升级前的状态一致性。

第二步是用目标版本的initdb初始化一个新数据目录。新目录需要为空或不存在,由initdb自动创建。可以根据实际环境指定字符集和区域参数,但升级前的检查会检测新旧集群的编码设置是否兼容。初始化完成后,可以把旧配置文件复制到新目录中以作参考,但其中的某些参数可能需要根据新版本调整。

# 使用新版本initdb初始化数据目录
/usr/pgsql-16/bin/initdb -D /pgdata/new -E UTF8 --locale=C

# 复制旧配置文件作为参考
cp /pgdata/old/postgresql.conf /pgdata/new/postgresql.conf.old
cp /pgdata/old/pg_hba.conf /pgdata/new/pg_hba.conf.old

第三步是运行升级前检查。检查模式会执行一系列验证,例如版本是否支持、目录是否可访问、扩展是否可用、文件系统是否允许硬链接等,但不会实际修改任何数据。只有检查全部通过后,才应继续执行正式升级。注意--check--link需要同时使用,这样检查过程才会按照硬链接模式的要求进行验证。

# 执行升级前检查
/usr/pgsql-16/bin/pg_upgrade \
  --old-bindir /usr/pgsql-14/bin \
  --new-bindir /usr/pgsql-16/bin \
  --old-datadir /pgdata/old \
  --new-datadir /pgdata/new \
  --link \
  --check

第四步是执行真正的升级。去掉--check参数后再次运行同样的命令。pg_upgrade会创建硬链接、升级系统目录并生成必要的辅助脚本。整个过程的耗时主要取决于数据库数量和系统元数据规模,而不是数据文件大小。升级完成后,pg_upgrade会在当前目录或指定输出目录生成analyze_new_cluster.sh等脚本。

# 正式执行硬链接模式升级
/usr/pgsql-16/bin/pg_upgrade \
  --old-bindir /usr/pgsql-14/bin \
  --new-bindir /usr/pgsql-16/bin \
  --old-datadir /pgdata/old \
  --new-datadir /pgdata/new \
  --link

第五步是调整配置并启动新集群。需要根据新版本修改postgresql.conf中的参数,例如shared_preload_librariesportunix_socket_directories等。确认无误后启动新实例,并登录检查版本号。升级完成后应及时运行统计信息收集脚本,避免因旧的统计信息不准确导致执行计划异常。

# 启动新集群
/usr/pgsql-16/bin/pg_ctl start -D /pgdata/new

# 确认新版本
/usr/pgsql-16/bin/psql -d postgres -c "SELECT version();"

# 收集统计信息
/usr/pgsql-16/bin/vacuumdb --all --analyze-in-stages

四、风险控制与回滚策略

硬链接模式最大的风险在于共享数据文件。新集群一旦启动并发生写入,旧集群的数据页就会被修改,旧版本不再能够安全启动。因此不能把旧数据目录当作可随时回滚的备份,这一点必须提前与业务方和运维团队沟通清楚。

如果升级流程在启动新集群之前失败,通常可以删除新数据目录后重新初始化,再从备份或旧目录重新执行升级。但一旦新集群已经写入过数据,回滚就只能依赖升级前的物理备份或逻辑导出。建议在升级前使用pg_basebackup或文件系统快照制作完整备份,并在测试环境完整演练一次升级和回滚操作。

另一个常被忽略的问题是表空间。如果旧集群使用了多个表空间,而这些表空间位于不同文件系统,硬链接模式会直接报错。此时可以将相关表空间迁移到同一文件系统,或者放弃--link改用普通复制模式。普通复制模式虽然较慢,但旧集群仍然可以在升级失败后继续使用,因为数据文件是完整复制而非共享。

五、常见问题与排查

最常见的报错是硬链接创建失败,错误信息中可能出现Invalid cross-device link。这表示旧数据目录和新数据目录不在同一文件系统上。使用df -T检查所有涉及目录,必要时调整新数据目录位置,或者将表空间链接到相同文件系统后再升级。

权限问题也经常导致失败。pg_upgrade必须以运行数据库服务的系统用户身份执行,通常是postgres。如果使用root执行,可能会因为新数据目录或旧数据目录的属主不正确而报错。确保两个数据目录以及pg_upgrade输出目录对该用户可读写。

扩展不兼容会在检查阶段暴露出来。报错会指出某个扩展在新版本中不可用或版本不匹配。解决方法是先在目标版本的扩展目录中安装对应扩展,再重新执行检查。如果某些扩展已经不再维护,需要在升级前从旧库中删除这些扩展及其依赖对象。

升级后如果发现数据库性能下降,不必立即怀疑升级过程。常见原因是统计信息过期,运行vacuumdb --all --analyze-in-stages后多数查询计划能够恢复正常。如果仍有异常,可通过pg_stat_statements定位具体SQL,而不是直接回滚。

pg_upgrade硬链接模式PostgreSQL升级修改时间:2026-08-24 01:09:39

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。