CentOS 7上PostgreSQL从9.2到15的pg_upgrade升级实战

发布时间:2026/9/17 3:21:45
CentOS 7上PostgreSQL从9.2到15的pg_upgrade升级实战 在 CentOS 7 上把 PostgreSQL 从 9.2 一次性升级到 15接到这个需求时我第一反应不是“能不能行”而是“这条路该怎么走才不翻车”。9.2 到现在已经停止维护很多年中间隔了整整十三个大版本很多配置文件、认证机制、系统目录结构都发生了翻天覆地的变化但好消息是PostgreSQL 官方提供了 pg_upgrade 这个专门用于跨版本升级的工具让这种看起来跨度极大的升级变得可行。这篇教程会把我在实际运维中完整的升级过程、踩过的坑、以及每一步背后的原因全部写出来给同样需要在 CentOS 7 环境下做 PostgreSQL 大版本升级的朋友一个可直接复用的操作参考。1. 升级动机与版本路线为什么直接跳到 151.1 9.2 已经是一台没有安全补丁的“裸奔服务器”很多 CentOS 7 服务器上的 PostgreSQL 9.2 都是当年最小化安装或者 yum 默认源直接带出来的能跑就一直没动过。但问题在于PostgreSQL 9.2 的官方支持周期早早就结束了安全漏洞反馈后不会有官方补丁更新这在等保合规、安全审计、外部漏洞扫描面前基本是扛不住的。另外从功能角度讲9.2 里的分区表、JSON 支持、索引管理都已经是十年前的水平新版本里很多性能优化和运维特性根本享受不到。所以这次升级的核心诉求很明确把系统自带的 9.2 替换成仍在维护周期内、功能完整的新版本同时尽可能缩短停机时间、降低数据迁移风险。这里有个前提必须先说清楚CentOS 7 的默认 yum 源里只有 PostgreSQL 9.2没有新版本所以必须引入官方维护的 PGDG 软件源后面会详细讲。1.2 为什么不选 10、12而是直接上 15有的朋友会想跨这么大的版本会不会风险太高不如从 9.2 先升到 10再升到 12再往上爬。这个思路听起来稳妥但实际上完全没有必要。PostgreSQL 官方给出的 pg_upgrade 设计目标就是支持从老版本直接升级到当前新版本本教程写作时 15 是稳定且经过大量生产验证的版本十六、十七也已经发布但核心迁移流程完全一样。选择 15 的主要理由有三个15 对 CentOS 7 的兼容性验证充分PGDG 仓库提供了完整的 rpm 包。团队使用的 JDBC 驱动、ORM 框架、BI 工具对 PostgreSQL 15 的适配已经非常成熟。15 引入了不少实用的特性比如更为完善的逻辑复制、更好的分区表性能、默认开启 scram-sha-256 密码认证等。直接一步到位升到 15省去了多次升级带来的重复工作量和多次停机的风险这也是我最终确定的路线。2. 升级前的盘点与风险排查备份、磁盘、扩展一个都不能少2.1 备份不是心理安慰最少留两条退路在动任何升级动作之前我坚持必须先做完整备份而且要留两条独立路径。第一份是用 pg_dumpall 做的逻辑备份覆盖所有数据库、角色、表空间定义第二份是对整个数据目录做文件级备份这样即使 pg_upgrade 把新集群搞坏了也能用旧数据目录直接把 9.2 拉起来回滚成本最低。pg_dumpall 的命令比较简单su - postgres pg_dumpall /backup/pg92_dumpall_$(date %F).sql文件级备份可以直接用 tar 打包但要先确认没有写入流量最稳妥的做法是停库后备份或者至少确保做备份期间没有业务写入systemctl stop postgresql tar czf /backup/pg92_datadir_$(date %F).tar.gz /var/lib/pgsql/data备份完以后一定要验证比如用pg_restore -l检查 dump 文件内容或者至少看看 tar 包能否正常列出文件列表。我见过有人备份流程走了十几遍结果恢复的时候才发现备份文件损坏这种低级错误在升级这种高风险操作里绝对不能出现。两条备份都确认无误后再继续往下走。2.2 磁盘、内存、目录结构一次性核对升级最容易被忽略的是磁盘空间。pg_upgrade 的默认模式是把旧集群的数据文件复制到新数据目录所以在升级期间磁盘上会同时存在两套数据简单估算就是至少需要额外一份数据目录大小的空间。如果打算使用 --link 硬链接模式虽然不需要复制数据文件但新旧目录必须在同一个文件系统上而且升级完成前旧目录绝对不能动。实际执行前我通常会跑这样几条命令评估df -h du -sh /var/lib/pgsql/data另外还有一个非常重要的检查点旧集群的 locale、编码、排序规则必须和新集群一致否则 pg_upgrade 预检阶段会直接报错。需要先记录下 9.2 里的实际值SHOW server_encoding; SHOW lc_collate; SHOW lc_ctype;后面初始化 15 的数据目录时这些值要保持完全一致。我遇到过有人因为 locale 不一致卡在预检阶段其实只要 initdb 时用同样的--localezh_CN.UTF-8或en_US.UTF-8这类参数就能解决。2.3 扩展与自定义对象的兼容性清单升级前需要盘点旧库里装了哪些扩展因为 pg_upgrade 检查阶段会验证新集群里是否已经准备好对应的扩展文件。如果旧库里有 dblink、hstore、pgcrypto、postgis 这类常用扩展而新集群里对应的 postgresql15-contrib 包没装预检就会失败。在 9.2 里用这条 SQL 快速列出所有数据库中的扩展SELECT datname, name, default_version, installed_version FROM pg_extension e JOIN pg_database d ON e.extnamespace d.oid;更常用的方式是在 psql 里切到对应数据库执行\dx。根据查到的扩展清单在安装 PostgreSQL 15 时把对应的扩展包一起装好。这里有个容易误操作的点新集群 initdb 之后不要在业务库手工执行CREATE EXTENSION否则系统目录版本可能和 pg_upgrade 预期的不一致。正确做法是只安装扩展的 rpm 包让 pg_upgrade 自己去处理扩展元数据和系统目录的升级。3. pg_upgrade 与 pg_dump两条迁移路线的选择和底层逻辑3.1 pg_upgrade 的底层逻辑不是重放数据而是搬家pg_upgrade 的工作原理和逻辑备份有本质区别。逻辑备份是先导出 SQL 语句再执行一遍相当于把仓库里的所有货物搬出来再一件件放进新仓库慢且容易出错pg_upgrade 则是直接把整个数据文件复制或硬链接到新版数据目录然后升级系统目录表catalog类似于整栋楼平移之后重新贴新标签。这种方式速度快得多而且对数据内容的兼容性要求主要在系统表层面而不是逐行检查数据。pg_upgrade 能支持从 PostgreSQL 8.4 以上版本直接升级到当前新版本所以 9.2 到 15 完全在官方支持范围内。不过它有硬性条件新旧版本二进制必须同时存在、数据目录路径必须分开、编码和 locale 必须一致、自定义扩展的共享库必须在新版本中准备好。这些条件只要提前梳理清楚整个升级过程其实很顺。另外pg_upgrade 在执行时会分别启动旧集群和新集群所以旧数据目录中不能有配置错误导致 PostgreSQL 9.2 无法启动。如果旧库的 postgresql.conf 里设置了特殊参数可能需要通过-o参数额外指定配置文件这一点在预检失败时看日志就能发现。3.2 pg_dump 全量逻辑迁移的适用边界逻辑迁移的优势在于简单直观不需要考虑系统目录兼容性适合数据量不大几十 GB 以内、停机窗口比较宽裕、或者顺便想重新整理表结构、清理无用数据的场景。但它有一个很大的缺点恢复时需要重建所有对象、索引、约束、外键数据量大时耗时非常长而且一旦恢复中间某条语句报错可能很难定位是数据问题还是环境问题。逻辑迁移的操作思路大致如下# 在 9.2 上导出 su - postgres pg_dump -Fc -f /backup/mydb.dump mydb # 在 15 上创建空库后恢复 su - postgres /usr/pgsql-15/bin/createdb -O myuser mydb /usr/pgsql-15/bin/pg_restore -d mydb /backup/mydb.dump3.3 我的选择建议三个维度做决策如果数据量在几十 GB 以内停机窗口允许团队对逻辑导出导入更熟悉可以用 pg_dump 路线灵活性高一些。如果数据量大、停机窗口紧、需要快速切换pg_upgrade 几乎是唯一合理选择。我这次面对的是几百 GB 的生产库所以毫不犹豫选了 pg_upgrade下面整个操作流程都以它为核心。4. PGDG 仓库安装与新版 PostgreSQL 15 初始化细节4.1 给 CentOS 7 添加 PGDG 软件源CentOS 7 默认源里没有 PostgreSQL 15必须先安装 PostgreSQL 官方维护的 PGDG yum 仓库。安装方法很简单直接用 rpm 安装仓库包yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm装完后可以验证一下yum list available | grep postgresql15如果能看到 postgresql15、postgresql15-server、postgresql15-contrib 这些包就说明仓库生效了。有些情况下 PGDG 仓库默认不是 enable 状态安装时显式指定一下yum install -y --enablerepopgdg15 postgresql15-server postgresql15-contrib需要注意不要卸载系统自带的 postgresql 9.2 包因为 pg_upgrade 执行期间需要调用旧版本的二进制文件卸载会让预检阶段直接无法启动旧集群。系统自带的 9.2 和 postgresql15 是不同包名安装新版时不会冲突服务名也完全不同这一点不用担心。4.2 新版服务的目录名、服务名与初始化方式安装完成后新版本的目录结构和服务命名都和 9.2 有区别这一点很多人容易搞混。9.2 的数据目录是/var/lib/pgsql/data服务名是postgresql.service15 的数据目录是/var/lib/pgsql/15/data服务名是postgresql-15.service二进制目录是/usr/pgsql-15/bin。初始化新集群前先把之前记录下来的 locale 参数用上/usr/pgsql-15/bin/postgresql-15-setup initdb这个脚本会自动以 postgres 用户执行 initdb 并生成数据目录默认参数通常没问题。如果之前的 9.2 使用了非默认 locale需要先用--locale显式指定或者手动执行 initdb 并传入对应参数。我这里英文环境的服务器用的是 en_US.UTF-8直接执行默认命令即可。初始化完成后先不要启动新服务也不要修改新数据目录里的任何文件等 pg_upgrade 检查通过再说。有个小细节新版 initdb 生成的 pg_hba.conf 默认对本地连接使用 peer 认证远程连接默认不允许这个在升级完成后再调整现在不碰它。5. pg_upgrade 全流程从预检、迁移到启动验证5.1 预检查先让工具把所有雷都排一遍这一步是整个升级过程中最重要的一环。预检查不会真正迁移数据只会启动两个版本实例做兼容性校验任何不满足的条件都会在日志里写清楚。先停掉旧库systemctl stop postgresql然后切换成 postgres 用户执行预检命令su - postgres /usr/pgsql-15/bin/pg_upgrade \ -b /usr/bin \ -B /usr/pgsql-15/bin \ -d /var/lib/pgsql/data \ -D /var/lib/pgsql/15/data \ -c参数说明如下-b旧版本 PostgreSQL 的可执行文件目录CentOS 7 自带 9.2 的二进制在/usr/bin。-B新版本 PostgreSQL 的可执行文件目录PGDG 安装后是/usr/pgsql-15/bin。-d旧数据目录/var/lib/pgsql/data。-D新数据目录/var/lib/pgsql/15/data。-c只做检查不实际迁移。如果预检通过输出会提示可以继续正式升级。如果失败需要查看当前目录下生成的pg_upgrade_server.log或者其他几个日志文件里面会写清楚是哪种兼容性问题。我遇到过最典型的问题一是 locale 不一致二是新集群没有安装旧库中的扩展包三是新旧数据目录的属主或权限不对。这些在日志里都会有明确提示按照提示修正后重新预检即可。5.2 正式迁移确认备份没问题再动手预检通过后正式执行迁移前再确认一次逻辑备份和数据目录备份都已经做好因为这一步之后旧集群数据可能被升级工具直接修改尤其是在使用硬链接模式的情况下不能再作为干净回滚点了。正式迁移命令就是把-c去掉su - postgres /usr/pgsql-15/bin/pg_upgrade \ -b /usr/bin \ -B /usr/pgsql-15/bin \ -d /var/lib/pgsql/data \ -D /var/lib/pgsql/15/data如果数据量非常大可以考虑加-k参数启用硬链接模式。硬链接模式不会复制数据文件而是把旧文件硬链接到新目录速度极快但前提是旧数据目录和新数据目录必须在同一个文件系统上而且升级完成后旧目录不能随意启动服务否则会污染新集群的数据文件。我的经验是如果磁盘空间够优先用默认的复制模式更稳妥如果数据量特别大且停机窗口非常短才考虑硬链接模式并且一定要保留额外备份。迁移完成后工具会提示执行analyze_new_cluster.sh脚本来重建统计信息这一步必须做否则查询计划会非常差。5.3 统计信息重建与启动新服务执行统计信息重建/usr/pgsql-15/bin/analyze_new_cluster.sh这个脚本本质上是调用vacuumdb --all --analyze-only对升级后的所有数据库重新分析统计信息。9.2 的统计信息数据结构在 15 里已经完全不同不重建的话优化器拿不到准确的表行数、索引选择性等关键数据会生成非常糟糕的执行计划。统计信息重建完成后启动新服务systemctl start postgresql-15 systemctl status postgresql-15如果启动失败先去查/var/lib/pgsql/15/data/log下的日志。最常见的启动失败原因就是配置参数不兼容比如旧配置文件里某些参数在新版已改名或废弃这也就是下一章要讲的配置迁移问题。6. 升级后的配置、认证与回滚问题6.1 postgresql.conf 里哪些参数要重建pg_upgrade 只迁移数据文件不会把 9.2 的 postgresql.conf 覆盖到新目录上新版的数据目录里是一份全新的默认配置。升级完成后需要把旧库中那些非默认参数迁移过来但不是直接拷贝文件因为很多参数在 15 里已经改名或者默认值变了。最稳妥的方式是逐个对比差异diff /var/lib/pgsql/15/data/postgresql.conf /var/lib/pgsql/data/postgresql.conf然后把旧库中有意调整过的参数对照新版语法重新写进去。这里有几个典型变化9.2 中的参数15 中对应的配置说明checkpoint_segmentsmax_wal_size/min_wal_size从 9.5 起按 WAL 大小控制不再用段数量wal_level hot_standbywal_level replicahot_standby 改名为 replicastandard_conforming_strings默认开启9.2 默认是 off现在是 on对字符串转义影响较大max_connections同参数名按需调整新版共享内存管理更灵活shared_buffers同参数名建议按物理内存的 20%-25% 设置logging_collector同参数名如果原来开了日志收集需要重新启用修改完 postgresql.conf 后需要重启服务生效。我的建议是分两步先只改必须改的共享内存和连接数参数让服务稳定跑起来再根据业务需要逐步优化其他参数避免一次改太多导致问题难以定位。6.2 pg_hba.conf 认证变化与一个容易忽略的密码坑PostgreSQL 15 默认的密码加密方式是 scram-sha-256而 9.2 默认是 md5。pg_upgrade 会把数据库角色及其密码哈希一起复制到新集群但一个容易被忽略的问题是如果旧角色存储的是 md5 格式的哈希而新版 pg_hba.conf 写的认证方法是 scram-sha-256那么客户端连接时很可能直接认证失败。解决方式有两种。如果希望保留旧密码且让应用无感知可以在 pg_hba.conf 中把对应的认证方法改为md5PostgreSQL 15 仍然支持这种认证协议。如果希望彻底切换到更安全的 scram-sha-256那就需要在升级后重新设置密码ALTER ROLE myuser WITH PASSWORD newpassword;另外新版默认的local连接使用peer认证即只能在操作系统用户和数据库用户名一致时本地免密连接。如果应用是通过 TCP 远程连接的还需要在 pg_hba.conf 中显式添加 host 规则否则连接会被拒绝。这一步很容易在升级后忽略结果应用侧报出一堆认证失败。我的做法是升级完成后先让运维配合在维护窗口内把应用连接串和认证配置都调一遍确保万无一失。6.3 数据校验、应用回归与回滚预案新服务正常启动后建议先做一轮基础校验检查数据库列表是否完整、角色权限是否保留、抽几张核心大表对比行数、看扩展是否都已正常升级。比如执行/usr/pgsql-15/bin/psql -h 127.0.0.1 -U postgres -d mydb -c \dx /usr/pgsql-15/bin/psql -h 127.0.0.1 -U postgres -d mydb -c SELECT count(*) FROM core_table;应用回归测试同样重要。需要注意PostgreSQL JDBC 驱动版本太旧可能无法连接 15需要升级到适配新版协议的版本。如果应用使用其他语言驱动也要一并检查兼容性。回滚预案在升级前就要想清楚。使用默认复制模式的 pg_upgrade 完成迁移后旧数据目录依然保留原样只要把新服务停掉重新启动 9.2 服务就能切回旧版本systemctl stop postgresql-15 systemctl start postgresql但如果使用了硬链接模式新旧数据文件是共享的升级后旧库不能直接启动回滚复杂度会高不少。所以再强调一遍如果对硬链接模式没有把握老老实实用复制模式。升级完成稳定运行一周之后确认业务无异常再决定是否清理旧数据目录不要急着删。