资讯动态

PostgreSQL小版本升级:源码编译方式从12.2到12.7完整实践

发布时间:2026/10/20 8:12:51 来源:尧图企业网站定制
提到PostgreSQL源码编译安装后的版本管理很多人第一反应就是pg_upgrade。但如果你只是想把12.2升级到12.7这种小版本升级pg_upgrade反而是错误的方向——小版本升级的核心是“只换二进制、不碰数据”。这篇我用一套源码编译方式部署的PG 12.2环境为例把12.2到12.7的完整升级过程走一遍覆盖升级前的信息采集、备份停库、源码重编译、二进制替换、启动验证和回滚预案。适合自己用手工编译方式搭过PG、现在要处理测试或生产环境小版本升级的朋友参考。1. 先搞清楚升级对象12.2到12.7到底动了什么1.1 版本号里的“12”意味着什么PostgreSQL的版本号由主版本号和小版本号组成。12.2里12是主版本号2是小版本号。主版本号决定了磁盘上数据文件的基本格式小版本之间的差异则只包含缺陷修复和安全补丁不引入新功能也不改变数据文件布局。这个规则是PG官方多年来一直坚持的兼容性承诺。你可以打开数据目录看一眼里面那个PG_VERSION文件内容只有一行12它只记录了主版本号。所以12.2升到12.7这个文件不用动数据文件不用动WAL日志的格式也保持兼容。你把旧的二进制停掉、换上12.7的二进制重新启动数据库就能继续用原来的数据目录启动起来。我在第一次做这种升级时也犯过嘀咕要不要先跑一遍initdb后来才彻底搞明白initdb是为全新部署准备的它会在指定目录生成一套全新的数据目录。升级场景下如果你手滑跑了initdb等于制造了一套空白数据文件原来的数据目录反而被晾在一边这是非常危险的误解。1.2 小版本升级不需要pg_upgrade的逻辑pg_upgrade这个工具设计出来的目的是处理跨主版本升级比如12升到13、13升到14。它的工作方式是先初始化一个新版本的数据目录再把旧数据以文件级迁移或逻辑转储的方式搬到新目录里。整个过程既需要空间也需要时间而且对生产环境来说这是一次真正的“数据搬家”。但12.2到12.7不存在格式差异数据目录完全可以直接复用。这个时候用pg_upgrade反而是把一次本可以五分钟完成的二进制替换硬生生升级成一次全量数据迁移风险和时间成本都成倍增加。两类升级的差别可以用下面这个表看得很清楚对比维度小版本升级12.2→12.7大版本升级12→13数据文件格式不变可能变化是否需要pg_upgrade不需要通常需要是否需要重新initdb不需要需要由升级工具处理数据目录是否重建不重建重新生成新目录停机窗口分钟级视数据量而定可能数小时主要风险点二进制替换、依赖库格式迁移、扩展兼容1.3 三个决定升级风险的现状检查虽然原理上说得很轻松但动手之前我建议你先回答三个问题。第一个问题是数据库里装了哪些扩展。比如PostGIS、TimescaleDB这类第三方扩展它们的.so动态库是用特定版本的PG头文件编译的小版本升级通常不需要重编但升级后必须做功能验证不能想当然认为它们一定还能正常加载。第二个问题是这套库有没有流复制从库。如果存在备库升级顺序就要反过来先升从库再做主备切换升原主库。单机场景照着本文操作没问题但一旦涉及复制拓扑直接只升主库会导致备用节点复制状态错乱。第三个问题尤其容易被忽略当前实例到底由谁来管理。是用pg_ctl手工拉起还是注册成了系统服务。服务脚本里写死的可执行文件路径以及环境变量都会影响你升级后启动实例的方式。这三件事先确认了后面整个过程心里才有底。2. 动手之前的准备还原configure现场、备份和干净停库2.1 用pg_config和config.status还原当年的编译参数源码编译安装的一个特点是二进制里“记住了”你编译时给configure传的参数。所以升级时最稳妥的第一步是先看一下当前12.2安装的编译参数/usr/local/pgsql/bin/pg_config --configure这条命令会输出一串--prefix... --with-...之类的参数我在实际操作中会先把它们完整复制出来存到升级记录文件里。如果你运气好当年编译的源码目录还在那更推荐去翻一下源码目录里的config.status它是configure执行后生成的最完整记录比我记忆中的参数列表可靠得多。拿到参数后逐项检查它们对系统依赖包的要求。比如--with-openssl需要本机有对应的开发头文件--with-libxml需要libxml2的开发包--with-readline需要readline开发包--with-perl和--with-python需要对应语言的嵌入开发支持--with-llvm需要LLVM工具链。这些依赖缺了某个编译阶段就会报错与其到时候现查不如升级前先对照参数列表把环境过一遍。2.2 逻辑备份加物理冷备怎么选怎么落地升级之前的备份我的习惯是做两层。第一层是逻辑备份用pg_dumpall把全库数据导出来落到另一块磁盘上。逻辑备份的粒度很细万一数据目录发生意外只要有这份转储数据层面的损失可以降得很低。缺点是库很大的时候恢复时间太长所以它只是兜底。第二层才是升级场景里真正的主力物理冷备。既然升级本来就要停库那停库之后顺手把整个数据目录打包几乎不增加额外窗口却能换来最完整、最快速的恢复手段。命令很简单tar -cf /backup/pgdata_122_before_upgrade.tar -C /data pgdata我通常不用-z压缩因为大目录边压缩边写磁盘会拖慢备份时间宁可多占一点磁盘空间也要尽快结束冷备。备份完成后我还会顺手跑一次tar -tf抽查几个关键文件在不在确认备份不是个空壳。这个习惯救过我一次那次确实是因为磁盘满导致tar“完美失败”了日志却没有立刻看出来。2.3 停库不是kill进程fast模式与收尾动作停库之前先看连接状况SELECT pid, state, query FROM pg_stat_activity WHERE backend_type client backend;如果看到长时间运行的事务先跟业务方确认不能直接压着长事务硬停。PG的fast模式会等正在运行的事务结束才关闭如果事务一直不结束pg_ctl stop会一直等下去。必要时要先取消活动查询或者让业务主动断开。确认连接清理干净后执行pg_ctl -D /data/pgdata stop -m fast等命令返回后去日志文件尾部确认一行关键记录database system is shut down。只有看到这行字才代表数据目录已经处于干净的关闭状态才能继续后续操作。另外提醒一下如果业务前面挂了连接池停库前最好把连接池切到暂停模式或维护模式。否则应用侧的连接池会在数据库重启后立刻发起一批重连请求这些请求积压在一起可能把刚启动的实例压出慢查询。这个细节在线上环境非常重要。3. 重新编译12.7参数对齐比编译本身更费心3.1 下载12.7源码并做校验从PG官网下载源码包文件名一般是postgresql-12.7.tar.gz。下载后不要急着解压先做完整性校验。把官网上公布的SHA256校验和保存下来然后本地执行sha256sum postgresql-12.7.tar.gz对比两边哈希值完全一致再继续。这一步看着多此一举但它能避免你拿到一个被篡改或下载损坏的源码包后者会在编译阶段制造一系列令人抓狂的诡异报错。3.2 configure、make、make install的注意点把源码解压到一个干净目录比如/opt/src/postgresql-12.7。注意千万别把源码目录放在PGDATA里也不要放在即将被替换的安装目录里避免升级过程中文件互相干扰。接着执行configure参数必须和从pg_config --configure里拿到的那一串保持一致尤其是--prefix。以我常用的目录为例完整命令长这样./configure --prefix/usr/local/pgsql \ --with-openssl \ --with-libxml \ --with-readline \ --with-llvmconfigure跑完后用make -j并行编译。-j后面的数字一般取CPU核数比如make -j8。PG源码全量编译在我常用的那台8核机器上大概五到八分钟能结束。如果中途报错不要慌去读终端输出里的具体提示大多数情况下是缺了某个开发包用系统包管理器补齐后重新configure即可。编译完成后的安装命令我建议用root执行make install这里有个非常常见的误解有人习惯用postgres用户编译然后发现make install没有权限写/usr/local/pgsql。其实编译阶段用普通用户完全没有问题但安装阶段要么切换root要么把安装目录的属主改成编译用户。这个细节如果没处理好容易在最后一步卡住而卡住时大家又容易怀疑编译有问题白白浪费时间。3.3 扩展模块在升级里的兼容性预判这一节专门说扩展是因为小版本升级踩坑概率最高的地方基本都在扩展上。PG自带的扩展模块比如pg_stat_statements、contrib下的各类模块会随源码一起编译安装这个没有问题。麻烦的是你自己后来手工安装的第三方扩展比如PostGIS。小版本之间PG内部ABI是稳定的所以理论上这些扩展的.so文件不需要重新编译直接就能加载。但“理论上”三个字意味着我要在升级后做一轮实际验证。验证方法很简单启动实例后执行SELECT name, installed_version FROM pg_extension;逐个检查扩展状态再实际跑一段依赖该扩展特性的SQL。如果某个扩展报出could not load library这类错误再去对应扩展官网找适配12.7的版本重装。只靠眼睛看永远不如让SQL真正跑一遍来得可靠。4. 替换二进制后的第一次启动体检、日志和业务验证4.1 启动前的文件体检启动新实例之前值得花三分钟做几项快速体检。第一步确认旧的数据库进程确实已经不存在pg_ctl -D /data/pgdata status如果返回no server running说明停干净了。第二步看数据目录的关键文件打开PG_VERSION确认还是12再用pg_controldata查看控制文件状态。第三步检查新二进制本身/usr/local/pgsql/bin/postgres --version ldd /usr/local/pgsql/bin/postgres | grep not foundldd那一步特别关键它会列出postgres可执行文件依赖的所有共享库。如果出现not found说明某个运行时库缺失启动必然失败。常见的缺失项包括libLLVM、libssl、libxml2等这些库通常在configure阶段没问题但运行时路径指向的系统库版本发生了变化导致新二进制找不到它们。最后一项是权限检查。确认PGDATA目录的属主是postgres用户而不仅仅是二进制文件属主。启动进程最终会以postgres用户身份去读写数据目录这一步做错启动时会直接报权限错误。4.2 启动、日志研判和版本确认体检通过之后正式启动pg_ctl -D /data/pgdata start -l /data/pgdata/log/pg_upgrade_12_7.log启动命令返回后不要着急去看日志文件。正常情况下日志里会出现两行关键记录一行是database system was shut down at ...说明新内核识别出了上次的干净关闭状态另一行是database system is ready to accept connections说明实例真正就绪。确认就绪后立刻执行SELECT version();只要返回结果里带着PostgreSQL 12.7内核替换这一步就已经成功了。如果启动失败也不要慌日志文件末尾会给出原因头部。常见的有三种postmaster.pid文件残留导致端口冲突、数据目录权限不对、某个共享库找不到。这三种问题处理起来都不复杂关键在于冷静看日志而不是盲目重启。4.3 业务侧验证清单内核起来只是第一步业务侧验证才算真正把关。我给一个自用的清单升级后按顺序过一遍逐库确认表数量和数据行数没有异常变化关键业务表抽查几个count(*)对每个已安装的扩展跑一次依赖该扩展的典型查询调用一次核心存储过程或触发器场景确认执行路径畅通查看pg_stat_activity确认应用重连后没有堆积大量空闲或报错连接扫描日志中从启动到验证结束这段时间内的ERROR、PANIC和FATAL记录如果应用侧有独立客户端也要在升级窗口内安排一次真实的业务请求。命令级别的SELECT 1只能证明内核活着证明不了业务链路里的那一堆SQL都正常。5. 回滚预案与三个容易被忽视的坑5.1 回滚好在只是换了个二进制小版本升级给人最大安全感的地方就在于回滚极其简单。因为数据目录从头到尾没有被改动过所以所谓的回滚只是把安装目录换回去而已。为了把回滚做利索升级前最好对整个安装目录做一次整目录备份cp -a /usr/local/pgsql /usr/local/pgsql.12.2bak一旦升级后验证不通过回滚操作就三步停库、换目录、启动。pg_ctl -D /data/pgdata stop -m fast mv /usr/local/pgsql /usr/local/pgsql.12.7new mv /usr/local/pgsql.12.2bak /usr/local/pgsql pg_ctl -D /data/pgdata start这里有个细节make install更新过的不仅bin目录lib和share目录里的文件也变了。所以如果你只备份bin目录回滚时share里的错误消息文件和扩展脚本还是12.7的虽然多数情况不影响运行但为了纯粹的一致状态整目录备份才是负责任的做法。5.2 坑一configure前缀不一致装到了“另一个PG”里这个坑是我亲眼见过最多的。升级者拿着旧的configure参数去编译结果不知道什么时候把--prefix路径写错了一截比如从/usr/local/pgsql写成了/usr/local/pgsql_new。编译安装全都成功但新的postgres二进制落在新目录里系统服务还在指旧目录。你以为升完级了实际上跑的还是12.2而新版本静静躺在另一个目录里无人问津。排查方法很简单升级后立刻检查which postgres和postgres --version确认路径和版本都对得上。如果发现版本没变第一反应不是怀疑编译而是怀疑安装位置。5.3 坑二JIT和LLVM环境漂移PG12开始支持基于LLVM的JIT编译。如果旧版本configure时带了--with-llvm数据库配置里也开了jiton那么新版本编译时必须延续同样的参数。否则编译时没有LLVM支持启动倒是正常但执行SQL时会遇到“JIT is not supported”类的报错业务方会莫名其妙。反过来也有另一个场景新环境里LLVM版本变了configure时虽然带上了--with-llvm但运行时动态库找不到匹配版本启动阶段就失败。处理方式只有一个方向让编译环境和运行环境的LLVM版本尽量一致或者彻底放弃JIT把jit参数改成off。我个人推荐后者除非你的业务对JIT有明确收益预期否则PG12场景下关闭JIT带来的运维复杂度下降远比那点性能加速更划算。5.4 坑三libpq版本没和对端对齐升级完成不代表所有东西都升级完了。服务器上如果还有其他应用直接链接libpq比如用C写的命令行工具、某些监控代理、或者你自己编译的周边程序它们引用的libpq.so源文件位于安装目录的lib下。make install之后磁盘上的libpq.so变成了12.7但正在运行的进程可能还在内存里跑着旧版本直到重启才会加载新库。更麻烦的是反过来如果内核对协议有微调而某个旧客户端还链着老版本的libpq可能出现连接失败或行为异常。所以升级窗口内我建议顺手做一件事检查哪些进程挂着libpq。lsof | grep libpq找出依赖它的应用在升级验证完成后安排一次重启让它们加载新版本的动态库。这个动作不复杂但能避免升级后第二天业务方报告“某个工具连不上库了”这种冤案。最后说一句我自己养成的习惯不管升级多小我都会把pg_config --configure的输出、PGDATA路径、安装目录权限、服务脚本位置这几样东西截图存档放进升级记录文件里。这次是12.2到12.7下次可能就是12.7到12.9。环境信息对不上时的排错成本远比多存一条记录高得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑