跳至内容

30.3 扩展与依赖升级

PostgreSQL 本体升级成功,扩展仍可能让新 cluster 无法启动、schema restore 失败,或更 隐蔽地在旧类型/索引上执行新二进制代码。原因是扩展不是一个对象,而是一条跨越软件 仓库、文件系统、配置、系统目录和业务数据的依赖链。

30.3.1 二进制包、数据库对象和预加载顺序

一项扩展至少有五层

示例升级问题
OS/packagepostgresql-18-postgis-*.so是否有目标 major/架构/OS 包
control/SQL files.control--1.0--1.1.sqldefault version 与 update path
preload/configshared_preload_libraries、GUC新 server 能否启动、是否需 restart
database catalogpg_extension.extversion、members每个 database 当前装了什么
stored data/indexextension type、operator class、index新代码能否解释旧物理表示

pg_extension 只回答第四层:

SELECT e.extname,
       e.extversion,
       n.nspname AS schema,
       r.rolname AS owner,
       e.extrelocatable
FROM pg_extension AS e
JOIN pg_namespace AS n ON n.oid = e.extnamespace
JOIN pg_roles AS r ON r.oid = e.extowner
ORDER BY e.extname;

还要查询目标 server 实际能提供的版本:

SELECT name,
       default_version,
       installed_version,
       comment
FROM pg_available_extensions
ORDER BY name;

SELECT name, version, installed, superuser, trusted
FROM pg_available_extension_versions
ORDER BY name, version;

“源端安装了 3.4,目标仓库有 3.5”并不能证明可直接升级;中间 SQL update scripts 可能缺失,C ABI 也可能不支持目标 major。

pg_upgrade 前先装文件,不要重建 SQL 对象

官方 pg_upgrade 流程要求先在新版本安装匹配的 extension shared libraries 与支持 文件。不要在空的新 cluster 预先执行:

CREATE EXTENSION postgis;

旧 cluster 的 extension catalog 与对象会随升级迁入,提前 CREATE 会重复。正确顺序 通常是:

锁定 old extension inventory
  -> 准备 target-major packages on every future node
      -> 检查 control / SQL / shared library
          -> 配置必要 preload,但暂不让错误配置影响 managed cluster
              -> pg_upgrade --check
                  -> pg_upgrade
                      -> 按生成脚本和扩展文档 ALTER EXTENSION UPDATE

pg_upgrade 会检查它能发现的 required libraries,也会为可用 extension update 生成 脚本;但官方明确指出外部模块的所有 binary compatibility 无法由它完全检查。包含自定义 background worker、WAL resource manager、shared memory 或特殊 table access method 的扩展,必须按扩展自己的 major-upgrade 文档测试。

preload 是启动依赖

盘点:

SELECT name, setting, source, pending_restart
FROM pg_settings
WHERE name IN (
  'shared_preload_libraries',
  'session_preload_libraries',
  'local_preload_libraries'
);

shared_preload_libraries 中任一 .so 缺失或与新 server ABI 不匹配,目标可能根本 起不来。先在隔离 cluster 加载:

postgres -D /isolated/new -C shared_preload_libraries

再实际启动并检查 log。不要在正式窗口里通过“先把所有 preload 清空”绕过;这样启动的 server 可能缺少审计、监控、时序、列存或业务所依赖的语义。

HA 集群中每个候选 primary/replica 都必须有同一套兼容库。只在当前 primary 安装, failover 才会暴露缺包。

30.3.2 扩展升级脚本与不可降级路径

先证明存在完整 update path

SELECT source, target, path
FROM pg_extension_update_paths('postgis')
WHERE source = (
  SELECT extversion
  FROM pg_extension
  WHERE extname = 'postgis'
)
ORDER BY target;

目标是看到从当前 installed version 到批准 target version 的路径。然后显式指定:

ALTER EXTENSION postgis UPDATE TO '3.x.y';

不带 TO 会使用 control file 的 default version;软件仓库下一次更新 default 后, 同一 runbook 可能得到不同结果。版本和包摘要都应固定。

ALTER EXTENSION UPDATE 执行扩展提供的 SQL scripts。脚本可能:

  • 修改 type/function/operator 签名;
  • 重写 extension-owned table;
  • 替换 operator class 或索引支持函数;
  • 迁移内部元数据;
  • 删除旧对象;
  • 要求先后执行专用 pre/post-upgrade procedure。

即使 SQL transaction 回滚成功,已更换的 .so、preload、外部文件和其他 database 状态也不一定一起回滚。更重要的是,许多扩展根本不提供 downgrade script。

降级能力要逐版本证明

对每个扩展写矩阵:

必答
package downgrade仓库是否保留 old major + old extension package
SQL downgrade pathpg_extension_update_paths 是否存在反向 path
on-disk format新版本写入后旧 binary 是否还能读
index rebuild哪些 index 必须重建,能否 concurrent
logical exportextension type 如何导出为 portable form
fallback回旧 cluster、restore,还是只能前滚

如果没有反向 path,发布决策应写:

before ALTER EXTENSION: old cluster / backup can still be rollback source
after extension writes new format: rollback requires restore or logical transform

不要尝试把 pg_extension.extversion 手工改回旧字符串;它只改 catalog 声明,不会逆转 SQL objects、内部表或存储格式。

扩展脚本也是受信任代码

升级脚本常以 extension owner 或 superuser 权限执行,binary 还能在 server 进程内运行。 包来源、签名/摘要、供应链和发布说明应进入变更评审。对 source superuser 不可信的 cluster,pg_upgrade/restore 还可能在目标执行源端预先布置的代码;隔离环境与代码 审阅不是可选项。

30.3.3 从 ch14 ADR 获取退出与兼容信息

第 14 章要求扩展准入时就记录 extension ADR。升级时不要重新从零猜用途,而应把 ADR 转成当前 inventory:

extension: postgis
business_owner: geo-platform
technical_owner: dba
installed_databases: [maps, routing]
installed_version: 3.x
target_version: 3.y
postgresql_majors: [17, 18]
os_arch: ubuntu24-arm64
packages:
  old: ...
  new: ...
preload: false
stored_types: [geometry, geography]
indexes: [gist, spgist]
upgrade_path_evidence: ...
downgrade_path: none
portable_export: EWKB/EWKT
exit_cost: high

将 ADR 与现场 catalog 对账:

ADR 有、catalog 无 -> 是否已退役但配置/包残留
catalog 有、ADR 无 -> 未治理依赖,升级门禁失败
version 不同 -> 漂移,先解释
preload 不同 -> 启动风险
package 无 target major -> 路径不可行

还要找出 extension members 与业务依赖:

SELECT e.extname,
       pg_describe_object(d.classid, d.objid, d.objsubid) AS member
FROM pg_extension AS e
JOIN pg_depend AS d
  ON d.refclassid = 'pg_extension'::regclass
 AND d.refobjid = e.oid
 AND d.deptype = 'e'
ORDER BY e.extname, member;

业务对象依赖 extension type/function/operator 的方向还需从 pg_depend 反查。只有列出 stored types、indexes、views、generated expressions 和 functions,才能知道升级失败 影响哪些对象。

退出策略在升级时兑现

ADR 若声明“可移除”,彩排应实际证明:

导出为 core PostgreSQL / portable representation
  -> 在无该 extension 的目标恢复
      -> 比较业务结果
          -> 重建目标索引
              -> 回放增量或冻结切换

若做不到,就把 exit cost 和供应商/社区生命周期写进升级风险。一个已停止支持 PG18 的 关键扩展,可能决定整个数据库只能停留在 PG17,或必须先做一次应用层去依赖迁移。

本章正式实验只迁移内建 plpgsql,升级后才安装 amcheck 验证两个 B-tree。它故意 不声称覆盖任何第三方扩展;生产票据必须用第 14 章 ADR 和真实包矩阵补齐这块证据。


上一节:三类大版本升级路径 · 返回本章目录 · 下一节:locale、collation 与索引风险 · 查看全书目录 · 查看索引中心

最后更新于