30.3 扩展与依赖升级
PostgreSQL 本体升级成功,扩展仍可能让新 cluster 无法启动、schema restore 失败,或更 隐蔽地在旧类型/索引上执行新二进制代码。原因是扩展不是一个对象,而是一条跨越软件 仓库、文件系统、配置、系统目录和业务数据的依赖链。
30.3.1 二进制包、数据库对象和预加载顺序
一项扩展至少有五层
| 层 | 示例 | 升级问题 |
|---|---|---|
| OS/package | postgresql-18-postgis-*、.so | 是否有目标 major/架构/OS 包 |
| control/SQL files | .control、--1.0--1.1.sql | default version 与 update path |
| preload/config | shared_preload_libraries、GUC | 新 server 能否启动、是否需 restart |
| database catalog | pg_extension.extversion、members | 每个 database 当前装了什么 |
| stored data/index | extension 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 UPDATEpg_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 path | pg_extension_update_paths 是否存在反向 path |
| on-disk format | 新版本写入后旧 binary 是否还能读 |
| index rebuild | 哪些 index 必须重建,能否 concurrent |
| logical export | extension 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 与索引风险 · 查看全书目录 · 查看索引中心