14.4 生命周期与升级耦合
扩展有两条相互耦合、却不会自动同步的生命周期:
node lifecycle:
repository -> package -> control/SQL/library -> preload/restart
database lifecycle:
CREATE EXTENSION -> member objects -> ALTER UPDATE -> DROP运维事故往往发生在两条线暂时分离时:包已升级而对象未升级、对象已写入 catalog 而新备库缺库、备份完整却恢复环境没有旧脚本。
14.4.1 安装版本不等于数据库对象版本
三个“版本”不要合成一个字段
至少记录:
| 名称 | 来源 | 示例 |
|---|---|---|
| 项目/release 版本 | upstream release/package metadata | pgvector 0.8.4 |
| 节点软件包 build | rpm/deb/image | vendor release + PG18 + OS |
| 数据库对象版本 | pg_extension.extversion | vector 0.8.4 |
有些包一次提供多个 SQL 对象版本和更新脚本。于是:
package release = newest support files
extversion = current objects in one database两者不同并不必然错误,但必须是被管理的过渡状态。
先装包,再逐库迁移
一个安全的普通更新顺序:
1. freeze exact package build
2. read control/release/update scripts
3. install package on standbys and primary nodes
4. verify files and loadability on every node
5. rehearse on restored/cloned database
6. inventory every database extversion/owner/dependency
7. establish query + plan + correctness baseline
8. ALTER EXTENSION ... UPDATE in controlled window
9. verify catalog/member/behavior/log/replication
10. observe before removing old support files为什么先把文件铺到所有节点?因为 failover 不应把一个刚完成对象更新的
数据库交给缺动态库的备库。物理复制会复制数据目录变化,不会替你把
$libdir 文件复制到另一台主机。
为什么逐库?扩展是 database-scoped。一个 cluster 中:
\l列出的每个数据库都有自己的 pg_extension;postgres 已经 1.6,不代表
app、analytics 或 template 也已经 1.6。
建立跨节点、跨数据库矩阵
节点侧:
pg_config --version
pg_config --sharedir
pg_config --pkglibdir
# 由包管理器/CMDB采集 exact build;不要解析一个浮动别名当版本数据库侧:
SELECT
current_database() AS database_name,
e.extname,
e.extversion,
pg_get_userbyid(e.extowner) AS owner,
n.nspname AS nominal_schema
FROM pg_extension AS e
JOIN pg_namespace AS n
ON n.oid = e.extnamespace
ORDER BY e.extname;聚合后应能回答:
node A/B/C package build
× database D1/D2/D3 extversion
× primary/standby role只保存 \dx 截图无法发现某台备库缺包,也无法显示其他数据库。
control 默认版本不会升级已有对象
升级包后:
SELECT
name,
default_version,
installed_version
FROM pg_available_extensions
WHERE name = 'pg_trgm';可能显示:
default_version=1.6
installed_version=1.3含义是:
- 新执行
CREATE EXTENSION pg_trgm默认创建 1.6; - 当前数据库对象仍是 1.3;
- 只有显式
ALTER EXTENSION ... UPDATE才迁移它。
不要靠重跑:
CREATE EXTENSION IF NOT EXISTS pg_trgm;期待升级。IF NOT EXISTS 只会 notice 并保留现有对象,也不保证同名现有
对象就是期望内容。
更新前读脚本,不只读 release notes
查看路径:
SELECT *
FROM pg_extension_update_paths('pg_trgm')
WHERE source = '1.3'
AND target = '1.6';再读取实际 package 中:
pg_trgm--1.3--1.4.sql
pg_trgm--1.4--1.5.sql
pg_trgm--1.5--1.6.sql评审:
- 是否改类型或存储格式;
- 是否重建/重写索引;
- 是否触碰扩展配置表;
- 是否删除/重命名函数与操作符;
- 是否会扫描业务数据;
- 需要何种锁;
- 失败是否能事务回滚;
- 更新后的旧应用是否仍兼容。
本章对这些文件和动态库做 SHA-256,保证复验时读的是同一构建;哈希不替代 代码评审。
package rollback 不等于 object rollback
假设已经把对象从 1.3 更新到 1.6,再把 OS 包降回只支持 1.3:
database extversion=1.6
filesystem supports only 1.3这是更危险的不一致。反向对象迁移只有在扩展明确提供 downgrade path 且 数据格式兼容时才可能。更常见的回退是:
- 在更新前保留可恢复备份/快照;
- 在 clone 上验证;
- 更新后向前修复;
- 若必须回退,恢复到更新前一致时间点并协调业务数据。
因此扩展更新的“可回滚”不能只写 apt downgrade。
14.4.2 大版本升级、备份恢复与逻辑复制兼容
pg_upgrade 不会替你验证外部模块
PostgreSQL 官方 pg_upgrade
文档明确提醒:所有外部模块必须与新 server 二进制兼容;pg_upgrade
无法检查这一点。新集群主库与备库都要安装匹配 shared libraries。
大版本升级前,对每个扩展冻结:
old server major/build
old extension object version
old package build
new server major/build
new-compatible package build
target extension object version
supported transition order可能的顺序取决于扩展:
old PG: update extension to prerequisite version
-> install new-PG-compatible files
-> pg_upgrade / logical migration
-> new PG: ALTER EXTENSION to target version也可能要求另一个顺序。以扩展的目标版本升级文档为准。
pg_upgrade --check 通过不等于扩展可用。clone rehearsal 至少要:
- 启动新集群;
- 查询每个扩展类型/函数;
- 检查 expression index/opclass;
- 重建或验证要求重建的索引;
- 跑应用回归;
- 启动新备库;
- 验证 dump/restore 和监控。
物理备份包含数据,不包含 OS 供应链
物理备份复制数据库文件和 WAL。它不会自动保存:
- PostgreSQL server binary;
- control 与版本 SQL;
- 动态库;
- preload 配置的外部部署来源;
- OS package repository。
恢复手册必须能重建:
compatible server binary
+ exact/compatible extension packages
+ configuration/preload
+ data directory and WAL只保留最新仓库,未必能恢复三年前依赖旧扩展对象版本的备份。长期保留策略要 考虑 package snapshot、image digest 或可复现构建。
逻辑 dump 用声明恢复扩展
PostgreSQL 把扩展视为整体。全库 schema-only dump 中,本章看到:
CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA shop_ch14;
COMMENT ON EXTENSION pg_trgm IS '...';
CREATE EXTENSION IF NOT EXISTS vector WITH SCHEMA shop_ch14;
COMMENT ON EXTENSION vector IS '...';却没有:
CREATE TYPE shop_ch14.vector ...
CREATE FUNCTION shop_ch14.similarity ...这正是扩展机制的 dump 合同。恢复顺序隐含要求:
target support files available
-> schema/extension creation
-> dependent application tables/indexes
-> data本章还证明一个容易忽略的选择性 dump 语义:
pg_dump --schema-only --schema=shop_ch14 ...输出包含:
candidate_doc table
vector column
GIN/HNSW indexes但不包含 CREATE EXTENSION。PostgreSQL pg_dump 文档对 --schema
选择明确警告:它不保证自动带上所选对象依赖的所有对象。这个 artifact
不能单独在洁净环境恢复,必须由恢复清单显式先创建扩展。
参见 pg_dump。
clean restore 是唯一有力的恢复证据
不要在原集群上执行 dump 后立刻宣称可恢复。洁净环境要求:
- 没有预装数据库扩展对象;
- 使用冻结 server/package build;
- 从空 database 开始;
- 按 runbook 恢复;
- 验证 extension owner/schema/version/member;
- 验证业务行数/checksum;
- 验证查询、计划与权限;
- 记录时间、日志和失败。
若恢复必须“手工试几个版本直到成功”,供应合同尚未完成。
逻辑复制不复制 schema 与扩展生命周期
内置逻辑复制主要复制表数据变更,不替你复制 DDL、extension control files
或 CREATE EXTENSION。发布端列使用自定义类型时,订阅端必须预先拥有可
接受该列值、函数和索引的兼容 schema。
评审:
- publisher/subscriber 类型名与语义;
- text/binary 传输与转换能力;
- extension object version;
- DDL 发布顺序;
- replica identity;
- extension-owned 配置/metadata 是否作为普通表复制;
- 订阅端触发器/默认值/约束的执行差异;
- major/architecture 组合。
“两端都显示 extension installed”仍不足以证明版本/数据语义兼容。
如果逻辑复制被用作迁移出口,先把自定义数据转换为内置交换类型通常更容易
控制。例如本章把 vector(3) 显式转为 text,而不是要求目标立即加载
同一 extension。
参见 Logical Replication Restrictions。
备库与 failover
物理 standby 会重放创建表、类型依赖和 extension catalog 变化,但不会 运行节点包管理器。变更前:
all standbys have compatible support files
-> preload/config staged
-> restart completed if required
-> primary database DDL/update
-> replication caught up
-> controlled switchover/failover probe检查不能只 SSH 到主库。新加入节点、灾备节点、延迟副本和备份 restore worker 都属于供应范围。
14.4.3 依赖扩展不可用时的降级策略
先区分必需能力与增强能力
扩展依赖可分:
| 类型 | 例子 | 不可用时 |
|---|---|---|
| 数据可读必需 | 业务列是自定义类型 | 数据库/查询可能无法正常使用 |
| 写入必需 | trigger/function 是写入合同 | 应停止写而不是绕过不变量 |
| 查询增强 | 可重建索引/opclass | 可回退较慢原生查询 |
| 观测增强 | 统计/采样扩展 | 核心业务可运行,诊断能力下降 |
| 维护增强 | repack/调度工具 | 延后维护并告警 |
只有后两三类适合真正“降级”。把自定义列类型说成可选能力是自欺。
设计能力探测,但不要每次请求查 catalog
发布/启动时探测:
SELECT
e.extname,
e.extversion
FROM pg_extension AS e
WHERE e.extname IN ('pg_trgm', 'vector');再验证所需签名与索引:
SELECT to_regprocedure('shop_ch14.similarity(text,text)');
SELECT to_regclass('shop_ch14.candidate_doc_title_trgm_idx');结果进入部署 gate 或低基数健康状态,而不是每个请求动态猜。应用 feature flag 必须与数据库迁移阶段同步:
extension absent:
extension-dependent feature disabled
extension installed and validated:
canary reads
index built and valid:
limited traffic
observation passed:
normal traffic可重建索引的降级
pg_trgm 例子:
normal:
title % $query
GIN gin_trgm_ops
degraded:
exact normalized equality
or prefix lookup
or PostgreSQL FTS降级查询语义不同,API 要明确:
- 是否返回较少结果;
- 是否暂停 fuzzy mode;
- 延迟是否提高;
- 哪些 SLO 暂时失效。
不要悄悄返回不同业务含义。
自定义类型的退场顺序
以 vector 为例:
1. freeze model/dimension and export format
2. add destination native/external representation
3. backfill with row/checksum verification
4. deploy dual-read or switched-read application
5. stop new extension-type writes
6. verify no view/function/index/table depends on type
7. drop extension-dependent indexes/columns
8. DROP EXTENSION without CASCADE
9. remove preload if any and then node package依赖检查:
-- 先看 pg_depend 和业务对象定义
\d+ shop_ch14.candidate_doc
\dx+ vector
-- 最后的 DROP 必须用 RESTRICT 语义暴露遗漏
DROP EXTENSION vector;绝不以:
DROP EXTENSION vector CASCADE;作为“清理方便”的生产脚本。CASCADE 会把尚未迁走的业务对象一起删除。
当动态库临时缺失
若 catalog 已有扩展而节点缺 library:
- 不要继续 failover 到该节点;
- 阻断相关流量或节点晋升;
- 从受控仓库恢复匹配包;
- 验证哈希、loadability 与查询;
- 检查所有其他节点是否同样漂移;
- 解释配置管理为何未发现;
- 完成恢复/备库回归。
删除 catalog 中 extension 不是修动态库缺失的第一反应,尤其当业务类型依赖 它时。
降级 SLO
ADR 预先定义:
feature: fuzzy-title-search
dependency: pg_trgm
failure_detection: deployment probe + query error alert
fallback: normalized-prefix-search
semantic_change: typo tolerance disabled
latency_budget: 200ms
maximum_duration: 2h
owner: search-team
restore_action: package parity + catalog/query verification没有时间、语义和 owner 的 fallback 只是愿望。
本节结论
扩展生命周期的真正完成条件:
can install
+ can update
+ can fail over
+ can back up and clean-restore
+ can cross major
+ can degrade or stop safely
+ can exit without CASCADECREATE EXTENSION 只完成第一项的一部分。
上一节:扩展选型的六个问题 · 返回本章目录 · 下一节:用 Pigsty 管理扩展可用性 · 查看全书目录 · 查看索引中心