博采众长:内核分支与扩展生态
14 博采众长:内核分支与扩展生态
PostgreSQL 的扩展生态很强,但“有这个扩展”不是架构理由,“能够安装”也 不是生产结论。一个扩展进入数据库后,可能同时改变:
- 节点上的软件包、控制文件、SQL 脚本与动态库;
- 数据库里的类型、函数、操作符、访问方法和系统目录依赖;
- 主库、备库、备份恢复、逻辑订阅与大版本升级的前置条件;
- 安装、升级和删除所需的特权;
- 应用数据的可移植性、故障半径与退出成本。
因此本章不做“常用扩展清单”,而是建立一套可复用的治理方法:
先证明问题,再检查原生替代;先冻结版本与运行条件,再创建数据库对象; 先演练升级、恢复和退出,再允许业务依赖。
第 15–17 章会分别深入检索、时空与分析/分布式能力。本章负责给它们提供 同一把尺子,避免每遇到一个新扩展就重新发明评审标准。
本章完成后
你应当能够:
- 区分 PostgreSQL server、发行版/内核、OS 软件包、扩展支持文件与数据库 扩展对象;
- 从
.control、版本 SQL、动态库与pg_extension解释一个扩展如何 被发现、安装和拥有; - 解释
superuser、trusted、relocatable、requires与shared_preload_libraries各控制什么; - 用
pg_available_extensions、pg_available_extension_versions、pg_extension_update_paths()与pg_depend取得原生证据; - 不把“兼容 PostgreSQL”误解为扩展、目录、运维和故障语义都兼容;
- 用六个问题筛选扩展,而不是按热度、功能数量或安装便利度决策;
- 区分软件包版本与数据库对象版本,并设计受控的
ALTER EXTENSION UPDATE; - 把备份恢复、物理备库、逻辑复制与
pg_upgrade纳入扩展生命周期; - 在 Pigsty 中区分仓库下载、节点安装、预加载配置和数据库启用;
- 解释包别名
pgvector与 SQL 扩展名vector为什么不能混用; - 识别节点漂移、control file 缺失、动态库缺失、未预加载与对象版本漂移;
- 写出包含问题、成功标准、供应链、权限、升级、恢复和退出的扩展 ADR;
- 对同一批候选给出“接受、限域试点、当前拒绝”三种有证据的结论。
三层状态,四个动作
扩展治理首先要拆开三层状态:
Pigsty 把典型流程组织成四个动作:
四步并非每个扩展都全部需要:
pg_trgm有数据库对象,但不需要 shared preload;vector有数据库对象和动态库,本章版本也不需要 shared preload;wal2json是逻辑解码插件,装包后按插件名使用,并不要求CREATE EXTENSION;- Citus、TimescaleDB 等扩展有额外预加载、拓扑或生命周期要求,必须查目标 版本文档,不能类推。
反过来,完成最后一步也不能证明前面三层在所有节点一致。主库已经
CREATE EXTENSION,某台备库仍可能缺动态库;数据库目录显示 0.8.4,
OS 仓库却已经换成另一构建。每层都需要独立证据。
贯穿实验:三个候选,三种结论
本章围绕 shop_ch14 评审三个候选:
| 问题 | 候选 | 结论 | 核心理由 |
|---|---|---|---|
| 单字段拼写容错 | pg_trgm |
accept | 问题有界、contrib、受信任、GIN 可证、退出不锁定列类型 |
| 语义近邻检索 | vector |
pilot | 能力成立,但模型、质量、资源、恢复与退出仍需真实语料证明 |
| 分布式分片 | citus |
reject now | 尚无单节点上限证据、分片键合同和跨分片事务设计 |
“拒绝”不表示 Citus 有缺陷。Pigsty 当前扩展目录提供 Citus,说明平台有 交付能力;本章仍拒绝它,是因为供应能力不能替代问题适配。
实验故意让权限差异可见:
随后由 pg36_owner 把 pg_trgm 从 1.3 更新到 1.6。更新前后:
这只证明固定五行夹具的机制与回归合同,不证明生产相关性、召回率或尾延迟。
实验资产
完整合同与入口:
- 实验合同
- 三项 ADR 摘要
- 可复用 ADR 模板
- 基础安装
- 管理员安装 vector
- 候选可用性
- 数据库扩展目录
- 扩展成员目录
- 版本与更新路径
- 索引目录
- 升级脚本
- 可移植文本导出
- 最终状态
- 自动审校器
- 精确复位
- Pigsty 4.5 声明片段
- v1.2 proposal
正式本地证据在 Homebrew PostgreSQL 18.6 上采集。正文把平台映射核对到 Pigsty 4.5,但没有在 Pigsty L1 主机上运行,因此输出明确记录:
这不是缺点掩饰,而是证据边界。读者在自己的 L1 上必须补采仓库、各节点 包版本、预加载和数据库对象状态。
快速运行
沿用前章受控的 libpq service:
all 会:
- 验证数据库、角色、ch04-v1 模型和基础业务不变量;
- 对目标 schema 与同名扩展执行 marker/owner/version 碰撞保护;
- 以 owner 安装受信任的
pg_trgm1.3; - 证明 owner 安装未受信任的
vector返回42501; - 由管理员安装
vector0.8.4,建立 GIN 与 HNSW 夹具; - 采集可用版本、control 属性、成员、ACL、索引和更新路径;
- 证明应用可查询但不能升级扩展;
- 记录升级前查询与强制索引计划;
- 把
pg_trgm更新到 1.6,再次记录相同证据; - 对 control、安装/更新 SQL 与动态库生成 SHA-256;
- 对比全库 schema dump 与
--schema=shop_ch14选择性 dump; - 把向量转为文本导出,形成试点退出材料;
- 验证错误 token、错误 target 和活跃 worker 下的 reset 拒绝;
- 不使用
CASCADE精确复位,再从零完整重建和复验。
成功摘要:
这个 proposal checksum 标识 ADR、版本、夹具和验收合同;运行时间、绝对 安装路径以及文件系统 inode 不进入业务 golden。
安全边界
task.sh all会删除并重建专用shop_ch14,并删除带本章精确 marker 的pg_trgm与vector。它只适合本书本地/开发夹具。生产安装和升级必须 使用分阶段迁移、备库检查、恢复演练、观察窗口和独立回退,不运行 “先删后建”的教学入口。
学习路径
14.1 PostgreSQL 扩展机制
先把扩展还原为 PostgreSQL 原生对象和支持文件。不了解这层,就无法解释 为什么“包已安装”和“数据库可用”不是同一件事。
14.2 内核、发行版与托管服务
再把扩展放回具体供应环境,建立 SQL、协议、目录、扩展与运维五层兼容矩阵。
14.3 扩展选型的六个问题
这一节把“喜欢哪个扩展”转化为六个可反驳、可采证的问题。
14.4 生命周期与升级耦合
安装只是生命周期起点。真正的承诺发生在升级、恢复、复制和退出时。
14.5 用 Pigsty 管理扩展可用性
把原生机制映射到 Pigsty 4.5,但始终回到节点文件、live 参数和系统目录 复核。
14.6 建立可复用扩展 ADR
把讨论沉淀为能够被后续章节复用、被版本变化触发复审的决策记录。
14.7 实战:评审三个候选扩展
最后把包、权限、对象、查询、升级、dump、出口和复位压成一份可审计交付物。
版本与证据边界
本章原理以 PostgreSQL 14–18 为范围;可执行 baseline 固定
pg_trgm 1.3/1.6、vector 0.8.4,并在 PostgreSQL 18.6 上验证。目标环境
没有这些精确版本时,不应伪造通过,而应复制 ADR、更新版本范围和 golden,
重新评审。
Pigsty 内容按 4.4 文档在 2026-07-29 核验。扩展目录、包版本和支持矩阵会 持续变化,实际变更前必须查目标 Pigsty 版本与仓库。
权威入口:
- PostgreSQL: Packaging Related Objects into an Extension
- PostgreSQL: CREATE EXTENSION
- PostgreSQL: ALTER EXTENSION
- PostgreSQL:
pg_available_extension_versions - PostgreSQL:
pg_extension - PostgreSQL: pg_upgrade
- Pigsty: Install Extensions
- Pigsty: Create Extensions
- Pigsty: Extension Package Aliases
- Pigsty: Extension Catalog
本章明确区分三种陈述:
- PostgreSQL/Pigsty 文档定义的机制;
- 本章针对三个问题作出的架构选择;
- 当前本地 evidence 实际证明的观察。
只有第三类能由 /tmp/pg36-ch14-final 或读者自己的 evidence 目录直接
复现。
上一章:言出法随:函数、触发器与存储过程 · 返回上卷导读 · 下一章:见微知著:全文、模糊与向量检索 · 查看全书目录 · 查看索引中心
14.1 PostgreSQL 扩展机制
CREATE EXTENSION vector; 只有一行,却横跨文件系统、权限系统、版本图和
数据库依赖。要治理扩展,必须先把这一行展开。
本节讨论 PostgreSQL 自己知道什么、不会替你知道什么。软件包仓库、容器与 Pigsty 映射留到后面。
14.1.1 control、SQL 脚本、动态库与对象所有权
一套扩展至少有两个身份
假设执行:
这里的 vector 是 SQL 扩展名。它不是项目名 pgvector,也不必等于
RPM/DEB 包名。PostgreSQL 根据 server 自己的安装目录寻找:
典型扩展由三类文件组成:
| 文件 | 作用 | PostgreSQL 何时使用 |
|---|---|---|
name.control |
元数据、默认版本、权限、依赖、可迁移性 | 发现与创建/更新扩展 |
name--version.sql |
创建这一数据库版本的成员对象 | CREATE EXTENSION |
name--old--new.sql |
从一个对象版本迁到另一个版本 | ALTER EXTENSION UPDATE |
| 动态库 | C 函数、hook、访问方法等运行代码 | 创建时或 backend 加载/调用时 |
纯 SQL 扩展可以没有动态库;只提供可加载模块的组件也可能没有
CREATE EXTENSION 接口。不能从扩展名猜文件组合,要读 control 与目标版本
说明。
查看当前 server 的查找位置:
这里的 pg_config 必须属于目标 server major。用 PATH 中另一个 PostgreSQL
版本的 pg_config 检查文件,可能得到一个完全正确、却与正在运行实例无关
的目录。
control 文件是创建合同
一个简化 control 文件可能表达:
关键字段:
default_version:未写VERSION时创建哪个对象版本;module_pathname:版本 SQL 中MODULE_PATHNAME的替换值,常指向$libdir下动态库;requires:必须先存在的其他扩展;superuser:安装脚本是否原则上要求超级用户;trusted:在superuser=true的前提下,是否允许具备当前数据库CREATE权限的非超级用户安装;relocatable:扩展整体是否允许换 schema;schema:若指定,强制成员安装到该 schema,并使扩展不可随意迁移。
这些是 具体已安装 control 文件 的事实,不是扩展项目永久不变的属性。 同名扩展升级后可以改变元数据,发行版也可能带不同补丁。用目录视图读取 当前 server 实际看到的值:
本章正式夹具观测到:
这是 PostgreSQL 18.6 + 当前本地支持文件的证据;读者环境必须重查。
CREATE EXTENSION 创建的是一个依赖边界
版本 SQL 可以创建类型、函数、操作符、访问方法、opclass、表或其他对象。 PostgreSQL 把这些对象登记为扩展成员。扩展本身记录在:
成员关系记录在 pg_depend,依赖类型为 e:
这个关系有三个后果:
- 成员通常不能绕过扩展被单独删除;
DROP EXTENSION会删除成员,即使没有写CASCADE;pg_dump通常用CREATE EXTENSION重建整组对象,而不是逐个 dump 成员定义。
第三点非常重要:备份文件能记录“需要 vector 0.8.4 的对象合同”,却不会 把 control、SQL 脚本和动态库塞进备份。恢复目标必须先拥有兼容支持文件。
nominal schema 不是容器
pg_extension.extnamespace 常被叫作扩展 schema,但它不是一个不可穿透的
容器。官方文档明确指出,扩展成员可能位于多个 schema;这个字段表示扩展
的 nominal schema。扩展名本身也不受 schema 限定:
relocatable=true 也不表示所有业务依赖都能无痛迁移。换 schema 会影响:
- 未全限定的函数与操作符解析;
- 固化在 view、expression index 或 function body 中的对象引用;
- 应用
search_path; - dump/restore 与旧迁移脚本;
- 安全审计假设。
把“目录允许 ALTER EXTENSION ... SET SCHEMA”与“应用兼容迁移”分开验证。
扩展 owner 与成员 owner
扩展有自己的 owner。通常创建者成为 extension owner;但安装脚本内部成员 的所有权还受脚本、可信安装规则和 PostgreSQL 版本语义影响,不能简单假设 “schema owner 拥有其中一切”。
本章把 pg_trgm 装在 shop_ch14:
由于本地 control 标记 trusted=true,有数据库 CREATE 权限的
pg36_owner 可以安装,extension owner 是 pg36_owner。安装脚本在受控
的高权限上下文中执行,使扩展能够创建所需对象。
这不是把任意第三方 SQL 交给普通用户安全执行。trusted 是扩展供应者和
发行者作出的安全承诺,管理员仍要审查来源、版本与安装 schema。
参见 Packaging Related Objects into an Extension
和 pg_extension。
14.1.2 普通扩展、预加载库与超级用户需求
“已创建”不等于“已加载”
按运行方式可以粗分四类:
| 类型 | 支持文件 | CREATE EXTENSION |
preload/restart |
|---|---|---|---|
| 纯 SQL 扩展 | control + SQL | 通常需要 | 通常不需要 |
| 按需加载的 C 扩展 | control + SQL + library | 通常需要 | 首次调用可加载 |
| 需要早期 hook 的扩展 | control + SQL + library | 通常需要 | 常需 shared preload |
| 非 SQL 插件 | library 或可执行组件 | 可能不需要 | 按子系统规则使用 |
扩展若要在 backend 初始化早期注册 shared memory、planner/executor hook、 background worker 或全局审计能力,往往需要:
这个参数在 server 启动时处理。修改后 reload 不够,通常需要滚动重启或
集群重启。库名拼错、文件缺失或二进制不兼容,可能直接阻止实例启动,所以
它是比普通 CREATE EXTENSION 更高风险的变更。
检查声明与 live 值:
只看配置仓库中的 YAML 不够;只看 SHOW 也不够。前者是意图,后者是当前
实例事实,还要在所有主备节点检查动态库。
pg_trgm 与本章的 vector 0.8.4 不需要 shared preload。不能由此推断
其他版本或扩展也不需要。Pigsty 当前文档列举 Citus、TimescaleDB、
pg_cron、pgaudit 等常见预加载场景,最终以目标扩展/版本文档和启动
实验为准。
superuser 与 trusted 是两道判断
pg_available_extension_versions 中:
表示只有超级用户可以执行创建/更新脚本。vector 在本章环境属于这一类:
得到:
管理员安装后:
extension owner 保持管理员角色。应用只获得使用所需类型、函数和表权限, 不获得扩展所有权。
对:
有数据库 CREATE 权限的非超级用户可以安装。安全关键点是:
- 安装脚本以 bootstrap superuser 的能力执行;
- extension owner 是调用者;
- 供应者必须保证非特权调用者不能借脚本选择、schema 或预置对象提权;
- 管理员应只信任随受控 PostgreSQL 发行版交付、且明确标记 trusted 的版本。
这就是为什么官方 CREATE EXTENSION 文档警告:从不可信来源安装扩展,相当
于以高权限运行其安装脚本。版本 SQL 能执行 DDL,也可能引用安装 schema
中预先存在的对象;安全安装应使用受控 schema 和受控 search_path。
权限分层
推荐把角色拆开:
本章证明:
返回:
应用能使用扩展能力,并不需要拥有生命周期控制权。
创建失败要按层定位
常见错误及第一检查点:
| 错误 | 更可能是哪一层 | 首查 |
|---|---|---|
| extension is not available | control 文件不可见 | pg_available_extensions、server sharedir |
could not access file $libdir/... |
动态库缺失/路径错 | pg_config --pkglibdir、各节点文件 |
| must be superuser | control 权限合同 | pg_available_extension_versions |
| must be loaded via shared_preload_libraries | 进程初始化条件 | pg_settings、重启状态、日志 |
| extension already exists | 数据库作用域冲突 | pg_extension |
| no installation script for version | 支持文件/版本图不完整 | available versions、包版本 |
| incompatible library | server major/CPU/ABI 错配 | 包构建、PG major、架构、启动日志 |
不要用反复 CREATE EXTENSION ... CASCADE 猜答案。先把错误归到供应、进程或
数据库层。
14.1.3 扩展依赖、版本与 ALTER EXTENSION
requires 是扩展依赖,不是 OS 包依赖
control 中:
表示数据库内扩展依赖。创建前可以显式安装:
也可以:
但生产治理不应默认 CASCADE,因为它会:
- 选择依赖的默认版本;
- 使用当前 schema/search path 决定安装位置;
- 扩大实际变更集合;
- 让审批只写一个扩展,实际多装若干对象。
更可审计的做法是显式列出依赖顺序、版本、schema、owner 和每步验证。
数据库依赖也不替代 OS 包依赖。目标 control 文件声明需要 foo,但节点上
仍必须先安装提供 foo.control、SQL 和 library 的软件包。
软件支持版本与对象版本分离
假设节点刚装入支持 1.6 的新包,而数据库仍显示:
此时:
这是正常的中间状态,不是 PostgreSQL 自动遗漏升级。装包不会主动在每个 数据库执行对象迁移;管理员必须逐库评审:
先列出版本:
再检查更新图:
本章得到:
PostgreSQL 会按可用更新脚本寻找路径;路径不是任意版本号比较。若没有从
当前对象版本到目标版本的脚本链,更新就不能发生。所谓“降级”同样需要明确
反向脚本,不能假设 ALTER EXTENSION ... TO old 会还原。
更新是 DDL 事务,不是无风险元数据改名
更新脚本可执行 DDL/DML,可能:
- 替换函数、操作符与类型支持函数;
- 增删成员;
- 改写扩展配置表;
- 获取对象锁;
- 使依赖表达式、索引或 cached plan 失效;
- 对大表触发长时间工作。
扩展脚本在一个隐式事务中运行,不能在其中自行提交,也不能把需事务外执行 的操作当普通更新步骤。即使脚本通常很快,也要把它当 schema migration:
更新 owner 才能 ALTER EXTENSION;某些脚本还因 control 权限需要更高
特权。本章由 pg36_owner 更新自己拥有的 trusted pg_trgm,而不是给
应用角色临时提权。
成员变化与 dump 语义
扩展维护者可以用:
调整成员关系。这不是业务迁移的日常捷径。成员一旦归入扩展:
- dump 通常不再单独保存其定义;
DROP EXTENSION会删除它;- 随手修改成员定义可能不会按预期进入 dump;
- 正确升级应通过新的扩展版本和 update script 交付。
检查成员,而不是只看 \dx:
本章 pg_trgm 1.3 有 37 个登记成员,更新到 1.6 后为 47;vector
0.8.4 在 PostgreSQL 18.6 夹具中有 237 个。成员数量用于发现本次环境漂移,
不是跨 PG major 的普适 golden。
本节结论
把 CREATE EXTENSION 读成一条完整声明:
少掉任何一段,扩展都只是“今天在这台主机上能用”,还不是可运营能力。
返回本章目录 · 下一节:内核、发行版与托管服务 · 查看全书目录 · 查看索引中心
14.2 内核、发行版与托管服务
“基于 PostgreSQL”可能表示使用上游源码加少量补丁,也可能只表示接受一部分 PostgreSQL wire protocol。两者对扩展的意义完全不同。
选扩展前先冻结运行载体:
扩展不是脱离这些条件存在的功能标签。
14.2.1 上游 PostgreSQL、补丁内核与兼容性承诺
“内核”至少要说明源码与构建
在本书中,上游 PostgreSQL 指 PostgreSQL Global Development Group 发布的 代码与版本语义。供应者可以在其上:
- 回移安全或缺陷补丁;
- 增加认证、存储、优化器或复制能力;
- 替换某些系统组件;
- 发布自己的包名、构建号与支持周期;
- 形成需要独立升级路径的 fork。
“补丁少”不自动等于二进制兼容;“版本号相同”也不证明动态库来自相同 ABI 与编译选项。C 扩展会与 server headers、符号、内存上下文、catalog 和内部 API 交互。PostgreSQL 不承诺跨 major 的内部 C ABI,扩展通常必须按目标 major 构建。
先采原始身份:
再采主机/包事实:
在 Pigsty 环境还要记录 pg_version、pg_mode、镜像/仓库快照与节点实际包
清单。不要只复制应用连接返回的 version();代理、兼容层或读写路由可能让
它不足以唯一标识整个集群。
扩展兼容承诺要逐项问
对一个补丁内核或 fork,至少问:
| 问题 | 为什么影响扩展 |
|---|---|
| 是否使用上游系统目录布局 | 扩展 SQL 可能查询/修改 catalog |
| 是否支持 PGXS 与上游 server headers | 决定能否按目标内核构建 |
| 是否保持所需 C symbols/hook | 决定动态库能否加载和正确运行 |
| WAL、存储和复制是否改动 | 自定义类型/访问方法能否在备库恢复 |
pg_upgrade 是否使用上游路径 |
外部模块与数据格式怎样迁移 |
| 由谁发布扩展包 | 上游扩展 release 不等于目标内核构建 |
| 谁承担联合支持 | 内核供应者和扩展供应者是否互相认可组合 |
“这个扩展支持 PostgreSQL 18”只说明扩展项目的一个范围;目标若是 PostgreSQL 18 衍生内核,仍需该组合的构建与验证证据。
SQL 扩展也不必然可移植
没有 C 动态库只能降低 ABI 风险,不能消除语义耦合。纯 SQL 扩展可能依赖:
- 特定系统目录列;
- 特定函数、数据类型或语法版本;
- planner 行为;
- event trigger;
- trusted extension 机制;
- superuser/owner 权限;
- 复制、dump 或安全策略。
因此兼容性不是“C 扩展危险、SQL 扩展安全”的二分,而是依赖面的大小。
支持矩阵要精确到组合
不要写:
而写:
同一扩展在 EL9/aarch64、Ubuntu/amd64 和某托管服务上是三个验证组合。
14.2.2 包仓库、容器镜像与托管白名单
包仓库解决供应,不替你做数据库升级
发行版包通常编码:
例如 Pigsty 的包别名 pgvector 可以映射到不同系统上的:
这层映射很有价值,但别名不是 SQL 名:
数据库中仍是:
仓库有包只证明“某个源声明可以供应”;还要验证:
- 目标 OS/PG major/架构是否有具体 artifact;
- repo metadata、签名与校验是否可信;
- 包是否已经同步到本地/离线仓库;
- 所有节点安装的 build 是否相同;
- control、更新 SQL 和动态库是否都随包出现;
- 升级包后每个数据库的
extversion是否仍需迁移。
锁定生产变更时,保存具体包 NEVRA/DEB version 或文件哈希,不只保存一个 会随仓库漂移的“latest”别名。
容器把供应快照化,但不把数据生命周期一起快照
容器镜像可以把 server 与扩展文件打包在同一 digest 中:
这有助于节点一致性,却有几个陷阱:
- 数据目录通常在持久卷,里面的
pg_extension.extversion不随镜像自动 更新; - 滚动换镜像期间,新旧 pod 可能同时服务,动态库 build 必须满足复制和 failover 条件;
- 恢复 job、备份验证 job 和临时维护容器也需要相同扩展文件;
- 镜像能启动不代表
ALTER EXTENSION UPDATE已完成; - 使用浮动 tag 会把可复现优势重新丢掉。
因此镜像 digest 是供应锁,不是数据库迁移状态。
托管白名单是产品合同
托管 PostgreSQL 常限制超级用户、文件系统和 server 参数。用户通常只能从 服务商允许列表中执行:
这带来不同问题:
- 是否允许该扩展;
- 允许哪个对象版本;
- 哪些区域、实例规格或 PG major 可用;
- 是否需要服务商参数组/重启;
- 谁控制更新窗口;
- 是否暴露扩展 owner;
- 是否允许自定义 schema;
- 备份、只读副本、跨区恢复与 major upgrade 是否支持;
- 从服务迁出时怎样导出自定义类型数据。
托管服务显示“支持 pgvector”,仍不能直接套用自建包的版本、参数和升级 步骤。白名单名称相同,控制面合同可能不同。
仓库、镜像与白名单的共同锁文件
为每个环境维护:
不是所有项目都要手写 YAML,但这些字段必须能从 CMDB、inventory、镜像 SBOM、evidence 或变更单还原。
14.2.3 “兼容 PostgreSQL”需要逐层验证
五层兼容模型
把“兼容”拆为五层:
| 层 | 要验证什么 | 典型误判 |
|---|---|---|
| SQL 语义 | 类型、函数、事务、隔离、DDL 行为 | 能跑简单 CRUD 就等于 PostgreSQL |
| Wire protocol | 驱动连接、认证、参数、错误字段 | 驱动能连就等于 server 等价 |
| Catalog/API | pg_catalog、扩展机制、统计视图 |
ORM 能用就等于管理工具能用 |
| Extension | control/SQL/C ABI、preload、成员与版本 | “支持 pgvector”就等于任意版本 |
| Operations | 备份、PITR、复制、failover、upgrade、监控 | 单实例功能测试代替生产生命周期 |
兼容声明必须说明通过了哪层。一个 wire-compatible 服务可能不提供
CREATE EXTENSION;一个支持扩展 SQL 接口的服务可能不允许用户控制版本;
一个上游二进制兼容内核仍可能在备份或升级控制面上有不同合同。
用需求驱动 probe
不要为了“全面”跑一堆无关 SQL。根据应用依赖形成最小 probe:
本章对 pg_trgm/vector 的 probe 包括:
这些 probe 仍没有覆盖备库与恢复,所以 evidence 不能写“生产兼容已验证”。
区分等价、适配与迁移
三个词不要混用:
- 等价:在声明范围内行为相同;
- 适配:应用通过条件分支、兼容层或限制使用范围后可运行;
- 迁移:接受行为变化并修改 schema、查询、运维或 SLO。
例如目标不支持 HNSW,但支持精确向量距离:
精确描述可以阻止“兼容”在采购、开发和事故处理中不断膨胀。
兼容矩阵
对候选平台填表:
| 验证项 | 上游自建 | Pigsty L1 | 托管候选 | 证据 |
|---|---|---|---|---|
| SQL 扩展名/版本 | catalog | |||
| package/build | 服务商托管 | package/API | ||
| preload/restart | live setting | |||
| owner/权限 | negative test | |||
| GIN/HNSW | catalog + plan | |||
| 物理副本 | failover test | |||
| schema-only dump | dump artifact | |||
| clean restore | restore report | |||
| major upgrade | cloned rehearsal | |||
| portable export | row/checksum |
空格不是“默认通过”,而是未验证。若某项与业务无关,可以标 N/A 并说明
理由;不能把它悄悄留空后宣称全兼容。
停止线
遇到以下任一情况,不进入生产:
- 无法唯一标识 server/扩展 build;
- 主备节点供应状态不一致;
- 只有创建成功,没有 clean restore;
- 目标服务商不能说明 major upgrade 时怎样处理扩展;
- 自定义类型无法导出为稳定交换格式;
- 兼容层不返回应用依赖的 SQLSTATE/事务语义;
- 供应者与扩展项目相互否认联合支持;
- 只能使用浮动包/tag,无法复现已测组合。
本节结论
“PostgreSQL 兼容”不是布尔值,而是一个带版本、层次和证据的向量:
扩展越深入类型、索引、hook 与存储,越不能只验证前两层。
上一节:PostgreSQL 扩展机制 · 返回本章目录 · 下一节:扩展选型的六个问题 · 查看全书目录 · 查看索引中心
14.3 扩展选型的六个问题
扩展评审最容易从产品介绍开始:
更好的起点是:
本节用六个问题形成漏斗:
- 具体问题和原生替代是什么?
- 成功、停止与反例怎样测量?
- 是否引入数据格式锁定,怎样导出退出?
- 备份、复制与升级生命周期是否成立?
- 维护、许可证与商业连续性如何?
- 权限、崩溃面和供应链风险能否接受?
前两问证明价值,中间两问证明可运营,后两问证明风险归属。任何一问没有 答案,都只能进入调查或限域试点,不能直接成为平台默认。
14.3.1 它解决的具体问题和原生替代是什么
问题必须可证伪
以下不是问题陈述:
它们已经把候选解写进需求。可评审的陈述应包含:
例如:
这时 pg_trgm 才是候选之一,而不是需求本身。
先列 PostgreSQL 原生替代
“原生”不等于永远更好,但它通常具有更小供应面。按问题检查:
| 问题 | 先检查 |
|---|---|
| 精确/前缀查找 | B-tree、表达式/partial index、规范化列 |
| 词项全文检索 | tsvector、GIN、词典与查询函数 |
| 范围/包含/重叠 | range/multirange、GiST、EXCLUDE |
| 半结构化属性 | jsonb + GIN/表达式索引,或重新建模 |
| 地理点/简单距离 | 内置 point 是否真的足够;复杂 GIS 再评 PostGIS |
| 时间分区/归档 | declarative partitioning、维护流程 |
| 容量问题 | 查询/索引修正、归档、分区、纵向扩容 |
| 批量分析 | 物化、并行查询、专用副本或外部分析系统 |
还要列应用/外部服务替代。一个扩展减少网络跳数,但把失败和升级绑定到 PostgreSQL;外部服务增加分布式复杂性,却可能提供独立扩缩容与专用算法。 这是工程权衡,不是“数据库内一定更快”。
比较单位是完整方案
不要比较:
而比较:
遗漏生命周期成本,会让扩展看起来永远最简单;遗漏外部同步成本,又会让 独立服务看起来永远更可扩展。
第二问:成功与停止怎样测
一个 PoC 至少同时有:
- 正确性/质量:结果集合、不变量、召回/精度或误差;
- 性能:P50/P95/P99、吞吐、build time、写放大;
- 资源:内存、磁盘、CPU、WAL、临时文件;
- 运行:备库延迟、恢复时间、升级锁、失败表现;
- 边界:数据规模、过滤选择性、并发、语言/模型/维度;
- 停止线:何时立即拒绝或回到替代方案。
本章五行 fixture 的成功标准故意很窄:
它证明 API 与索引机制,不证明:
因此 pg_trgm 的接受范围只是“有界单字段模糊匹配”;vector 仍是 pilot。
反例必须进入数据集
只测成功样本会让任何扩展通过。检索候选至少加入:
- 短字符串、空值、重复值;
- 不同语言、大小写、重音和规范化;
- 高频词、低选择性谓词;
- 过滤后很少/很多候选;
- 大批更新与删除;
- 冷缓存、热缓存;
- 与业务谓词组合的真实查询。
分布式候选则要加入:
- 跨分片事务;
- 热分片;
- rebalance;
- 节点失联;
- DDL 传播;
- 全局唯一性与引用完整性;
- 备份、恢复和扩缩容窗口。
问题没有对应反例,PoC 更像演示。
14.3.2 数据格式是否锁定、能否导出和退出
第三问:锁定发生在哪里
扩展可能只增加可重建索引,也可能让业务列使用自定义类型:
| 依赖 | 锁定程度 | 退出方式 |
|---|---|---|
| 纯函数、无持久数据 | 低 | 改查询后删除 |
| 可重建 expression/index/opclass | 较低 | 先换查询/索引,再删除 |
| extension-owned 配置表 | 中 | 导出、转换、重建 |
| 自定义列类型 | 高 | 列级转换或交换格式迁移 |
| 自定义 table/access method | 高 | 全表重写/逻辑迁移 |
| 存储/WAL/分布式元数据 | 很高 | 专用迁移与拓扑退场 |
pg_trgm 在本章只提供函数、操作符与 GIN opclass,业务 title 仍是
text。退出可以先改查询、删除 GIN,再删除扩展。
vector 让:
成为列类型。只要该列存在:
就会因依赖失败;使用 CASCADE 会把业务对象一起删除,不是可接受的退出。
在采用前写出出口
本章先建立可移植导出:
输出形如:
这只建立一个交换入口。真正退出还要回答:
- 文本格式由谁解析,精度是否损失;
- 行数、主键和 checksum 怎样核对;
- 目标类型是什么;
- 应用何时双读/双写;
- ANN 索引何时停止使用;
- 大表转换是否重写、锁多久、产生多少 WAL;
- rollback 点在哪里;
- 备份中最后一个扩展依赖何时消失。
没有跑过迁移的“理论可导出”只能算风险缓解,不能算完成退出演练。
不把逻辑 dump 当数据出口
全库 pg_dump 通常写:
这仍要求恢复端安装 vector。它是 同构恢复 合同,不是脱离扩展的出口。
真正的 portability artifact 应使用目标系统可理解的格式:
- 内置
text/numeric/jsonb/array; - CSV/JSON/Parquet 等交换格式;
- 明确坐标系、单位、模型与版本的领域格式;
- 行数、范围和 checksum。
例如 PostGIS 几何不能只导出一个没有 SRID 的坐标字符串;embedding 不能 只导出数字而丢掉 model、dimension、normalization 与 distance metric。
第四问:生命周期是否成立
锁定不只发生在数据格式,也发生在运维路径。逐项问:
只有 happy-path CREATE EXTENSION 的项目,生命周期证据为零。
退出预算
把退出成本量化:
| 项目 | 估算 |
|---|---|
| 需要转换的数据量 | bytes / rows |
| 双写窗口 | hours/days |
| 额外存储 | old + new + indexes |
| 最大锁窗口 | seconds/minutes |
| WAL 与备库延迟 | projected/tested |
| 应用版本跨度 | N / N+1 compatibility |
| 回滚最晚点 | before/after backfill/switch |
| 人员与演练时间 | owner + date |
如果退出成本已经超过系统可承受窗口,决策不是“以后再说”,而是当前已经 形成实质锁定,必须由业务负责人接受。
14.3.3 维护活跃度、许可证与商业连续性
第五问不是“最近有没有 commit”
维护健康至少包括:
- 是否有明确维护者和 release 流程;
- 对当前/未来 PostgreSQL major 的响应速度;
- 缺陷、崩溃和安全问题是否被分类与修复;
- release notes 与 update scripts 是否完整;
- CI 是否覆盖目标 OS/架构/PG major;
- 文档是否说明备份、升级、preload 和限制;
- issue/PR 是否有持续 triage;
- 是否存在多名可发布维护者;
- 旧版本支持与 EOL 策略是否清晰。
“每天很多 commit”可能只是功能开发;“半年没 commit”也可能是成熟稳定。 用与你的风险相关的证据判断。
许可证检查三个层次
至少分别检查:
- 源码许可证;
- 二进制包与捆绑依赖;
- 企业使用、再分发、托管服务或商业功能条款。
不要从项目名称、GitHub 页面徽章或旧博客推断。保存目标版本的 LICENSE、 NOTICE、依赖清单与法务结论。许可证可能随 major、模块或商业发行版变化。
技术上能装,不表示组织有权按计划分发;开源,也不表示所有附加服务和 品牌条款相同。
商业连续性不是“有公司背书”
公司支持可以降低某些风险,也引入:
- 定价或授权变化;
- 产品方向与开源版分叉;
- 单一 vendor build;
- 支持合同终止;
- 收购、停服或仓库下线。
社区项目则可能有 bus factor、发布带宽和联合支持问题。两者都要问:
答案不一定要求团队自己维护 fork,但必须有时间与责任人。
建立维护快照
ADR 中保存带日期的证据:
维护状态会变化,所以结论必须有复审日,不能把一次评估写成永久事实。
14.3.4 权限、崩溃面与供应链风险
第六问:谁获得什么能力
扩展评审画出特权链:
每一环都可能改变下一环执行的代码。要记录:
- 谁能把包加入仓库;
- 谁能改
shared_preload_libraries和重启; - 谁能
CREATE/ALTER/DROP EXTENSION; - extension owner 是谁;
- 成员函数默认给
PUBLIC什么权限; - 应用通过哪些 schema、函数、操作符和类型使用;
- 谁能在安装 schema 预置同名对象影响脚本解析。
C 扩展与 server 共享故障域
C 扩展不是旁路微服务,它在 PostgreSQL 进程地址空间内运行。缺陷可能导致:
- backend crash;
- postmaster 重启其他 backend;
- 内存破坏;
- 错误结果或数据损坏;
- 无限循环/资源耗尽;
- 特权边界漏洞。
这不表示拒绝所有 C 扩展。PostgreSQL 大量核心能力也使用 C;结论是:
一个 in-process 扩展的审核与发布等级,应接近数据库 server 组件,而不是 普通 SQL 库。
需要:
- 来源与构建可追溯;
- 目标 major/架构测试;
- crash/recovery 与备库测试;
- 资源上限;
- 安全通告与快速替换能力;
- core dump/日志/回滚预案。
纯 SQL/PL 扩展没有任意 C 内存访问,但仍可能包含提权、search_path、
动态 SQL、错误 ACL、超大查询和对象劫持风险。
供应链不止校验下载文件
最小证据链:
本章 package manifest 对这些具体文件做 SHA-256:
哈希能发现漂移,不能证明代码安全。它必须与仓库签名、构建来源、审计和漏洞 响应组合。
风险分级
一个实用起点:
| 等级 | 例子 | 最低门槛 |
|---|---|---|
| L0 可重建 SQL/索引 | 不改变持久类型、无 preload | 功能/计划/恢复/退出 |
| L1 自定义类型/C 函数 | 持久列依赖动态库 | 供应锁、主备、clean restore、出口 |
| L2 preload/hook/worker | server 启动与全局执行路径 | 滚动重启、crash/failover、资源与禁用 |
| L3 存储/分布式拓扑 | WAL、shard、专用 catalog | 完整故障模型、升级/回退、联合支持 |
等级不是产品好坏,而是证据成本。高等级候选可以采用,但不能用 L0 的 “创建成功”验收。
决策状态
只允许清晰状态:
避免“原则同意”“先装上再看”“有需要都可以用”这类无法执行的结论。
本节结论
六问的顺序很重要:
越早失败,越应尽早停止。没有实际问题时,不需要花几周证明供应链;问题 成立后,也不能用性能收益跳过恢复和退出。
上一节:内核、发行版与托管服务 · 返回本章目录 · 下一节:生命周期与升级耦合 · 查看全书目录 · 查看索引中心
14.4 生命周期与升级耦合
扩展有两条相互耦合、却不会自动同步的生命周期:
运维事故往往发生在两条线暂时分离时:包已升级而对象未升级、对象已写入 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 对象版本和更新脚本。于是:
两者不同并不必然错误,但必须是被管理的过渡状态。
先装包,再逐库迁移
一个安全的普通更新顺序:
为什么先把文件铺到所有节点?因为 failover 不应把一个刚完成对象更新的
数据库交给缺动态库的备库。物理复制会复制数据目录变化,不会替你把
$libdir 文件复制到另一台主机。
为什么逐库?扩展是 database-scoped。一个 cluster 中:
列出的每个数据库都有自己的 pg_extension;postgres 已经 1.6,不代表
app、analytics 或 template 也已经 1.6。
建立跨节点、跨数据库矩阵
节点侧:
数据库侧:
聚合后应能回答:
只保存 \dx 截图无法发现某台备库缺包,也无法显示其他数据库。
control 默认版本不会升级已有对象
升级包后:
可能显示:
含义是:
- 新执行
CREATE EXTENSION pg_trgm默认创建 1.6; - 当前数据库对象仍是 1.3;
- 只有显式
ALTER EXTENSION ... UPDATE才迁移它。
不要靠重跑:
期待升级。IF NOT EXISTS 只会 notice 并保留现有对象,也不保证同名现有
对象就是期望内容。
更新前读脚本,不只读 release notes
查看路径:
再读取实际 package 中:
评审:
- 是否改类型或存储格式;
- 是否重建/重写索引;
- 是否触碰扩展配置表;
- 是否删除/重命名函数与操作符;
- 是否会扫描业务数据;
- 需要何种锁;
- 失败是否能事务回滚;
- 更新后的旧应用是否仍兼容。
本章对这些文件和动态库做 SHA-256,保证复验时读的是同一构建;哈希不替代 代码评审。
package rollback 不等于 object rollback
假设已经把对象从 1.3 更新到 1.6,再把 OS 包降回只支持 1.3:
这是更危险的不一致。反向对象迁移只有在扩展明确提供 downgrade path 且 数据格式兼容时才可能。更常见的回退是:
- 在更新前保留可恢复备份/快照;
- 在 clone 上验证;
- 更新后向前修复;
- 若必须回退,恢复到更新前一致时间点并协调业务数据。
因此扩展更新的“可回滚”不能只写 apt downgrade。
14.4.2 大版本升级、备份恢复与逻辑复制兼容
pg_upgrade 不会替你验证外部模块
PostgreSQL 官方 pg_upgrade
文档明确提醒:所有外部模块必须与新 server 二进制兼容;pg_upgrade
无法检查这一点。新集群主库与备库都要安装匹配 shared libraries。
大版本升级前,对每个扩展冻结:
可能的顺序取决于扩展:
也可能要求另一个顺序。以扩展的目标版本升级文档为准。
pg_upgrade --check 通过不等于扩展可用。clone rehearsal 至少要:
- 启动新集群;
- 查询每个扩展类型/函数;
- 检查 expression index/opclass;
- 重建或验证要求重建的索引;
- 跑应用回归;
- 启动新备库;
- 验证 dump/restore 和监控。
物理备份包含数据,不包含 OS 供应链
物理备份复制数据库文件和 WAL。它不会自动保存:
- PostgreSQL server binary;
- control 与版本 SQL;
- 动态库;
- preload 配置的外部部署来源;
- OS package repository。
恢复手册必须能重建:
只保留最新仓库,未必能恢复三年前依赖旧扩展对象版本的备份。长期保留策略要 考虑 package snapshot、image digest 或可复现构建。
逻辑 dump 用声明恢复扩展
PostgreSQL 把扩展视为整体。全库 schema-only dump 中,本章看到:
却没有:
这正是扩展机制的 dump 合同。恢复顺序隐含要求:
本章还证明一个容易忽略的选择性 dump 语义:
输出包含:
但不包含 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 变化,但不会 运行节点包管理器。变更前:
检查不能只 SSH 到主库。新加入节点、灾备节点、延迟副本和备份 restore worker 都属于供应范围。
14.4.3 依赖扩展不可用时的降级策略
先区分必需能力与增强能力
扩展依赖可分:
| 类型 | 例子 | 不可用时 |
|---|---|---|
| 数据可读必需 | 业务列是自定义类型 | 数据库/查询可能无法正常使用 |
| 写入必需 | trigger/function 是写入合同 | 应停止写而不是绕过不变量 |
| 查询增强 | 可重建索引/opclass | 可回退较慢原生查询 |
| 观测增强 | 统计/采样扩展 | 核心业务可运行,诊断能力下降 |
| 维护增强 | repack/调度工具 | 延后维护并告警 |
只有后两三类适合真正“降级”。把自定义列类型说成可选能力是自欺。
设计能力探测,但不要每次请求查 catalog
发布/启动时探测:
再验证所需签名与索引:
结果进入部署 gate 或低基数健康状态,而不是每个请求动态猜。应用 feature flag 必须与数据库迁移阶段同步:
可重建索引的降级
pg_trgm 例子:
降级查询语义不同,API 要明确:
- 是否返回较少结果;
- 是否暂停 fuzzy mode;
- 延迟是否提高;
- 哪些 SLO 暂时失效。
不要悄悄返回不同业务含义。
自定义类型的退场顺序
以 vector 为例:
依赖检查:
绝不以:
作为“清理方便”的生产脚本。CASCADE 会把尚未迁走的业务对象一起删除。
当动态库临时缺失
若 catalog 已有扩展而节点缺 library:
- 不要继续 failover 到该节点;
- 阻断相关流量或节点晋升;
- 从受控仓库恢复匹配包;
- 验证哈希、loadability 与查询;
- 检查所有其他节点是否同样漂移;
- 解释配置管理为何未发现;
- 完成恢复/备库回归。
删除 catalog 中 extension 不是修动态库缺失的第一反应,尤其当业务类型依赖 它时。
降级 SLO
ADR 预先定义:
没有时间、语义和 owner 的 fallback 只是愿望。
本节结论
扩展生命周期的真正完成条件:
CREATE EXTENSION 只完成第一项的一部分。
上一节:扩展选型的六个问题 · 返回本章目录 · 下一节:用 Pigsty 管理扩展可用性 · 查看全书目录 · 查看索引中心
14.5 用 Pigsty 管理扩展可用性
Pigsty 能把仓库、包、配置和数据库声明统一管理,但平台声明不是 PostgreSQL 事实的替代品。正确用法是:
本节以 Pigsty 4.5 文档为基线。参数与扩展目录会变化,目标集群变更前应使用 同版本文档和 inventory,而不是照抄本章时间点。
14.5.1 包、仓库、模板与节点差异
Pigsty 中的四步模型
当前 Pigsty 扩展文档把过程分成:
这与上一节三层状态一致:
| Pigsty 动作 | 主要对象 | 原生验证 |
|---|---|---|
| Download | repo/cache | repo metadata、artifact、checksum |
| Install | node package/files | package inventory、control/library |
| Configure | Patroni/PostgreSQL 参数 | pg_settings、restart、日志 |
| Create | database object | pg_extension、成员、查询 |
不是每个扩展都需要 preload,也不是每个已安装插件都需要创建数据库对象。
pg_packages 与 pg_extensions
Pigsty 4.5 文档区分:
pg_packages:通用基础包组,通常用于所有 cluster 的核心组件;pg_extensions:特定 PGSQL cluster 需要的扩展软件包,初始化时安装, 也可对已存在 cluster 执行pg_extensiontag 收敛。
示例:
对于已运行 cluster,先修改受版本控制的声明,再执行目标明确的 playbook:
临时命令行覆盖:
适合受控应急或实验,但若不回写 inventory,下一位维护者看不到持久意图。
参见 Pigsty: Install Extensions。
包别名是跨发行版映射
Pigsty 使用稳定别名:
映射到 PG major 与 OS 对应包,例如:
还提供 pgsql-rag、pgsql-gis、pgsql-fts 等类别别名。类别安装范围大,
实验便利不等于生产应一次装整类。生产 ADR 优先列精确候选,避免供应面无意
扩大。
包别名与 SQL 名必须在清单中同时保存:
| 项目 | package alias | SQL extension |
|---|---|---|
| pgvector | pgvector |
vector |
| PostGIS | postgis |
postgis、postgis_topology 等 |
| pg_trgm | 随 pgsql-main/默认包集合供应 |
pg_trgm |
参见 Pigsty: Extension Package Aliases。
默认供应与默认启用不是同一层
Pigsty 4.5 当前默认文档说明:
pgvector随默认pgsql-main包集合安装;pg_trgm位于pg_default_extensions,默认在数据库的publicschema 启用;pg_stat_statements、auto_explain等进入默认 preload/观测集合。
这些默认值会随 Pigsty release 演进。目标环境要检查自身 inventory,而不是 从“4.4 默认”反推一个已升级多次的 cluster。
还要注意:若 pg_trgm 已在数据库 public 创建,就不能再在
app_ext 创建第二份同名扩展。自定义 schema 前必须协调
pg_default_extensions,不能让两个声明互相竞争。
参见 Pigsty: Default Extensions。
仓库可达性与本地仓库
在线环境可能直接从配置的上游/第三方仓库下载。受限环境通常由 Pigsty infra 节点维护本地软件仓库。无论哪种模式,验证:
“主库现在有文件”不能证明:
- 新副本能加入;
- 灾备站点能恢复;
- 离线仓库仍保留旧版本;
- major upgrade 目标包已经可用。
扩展变更单应附 repo snapshot/version,而不只附公共下载 URL。
参见 Pigsty: Download Extensions。
节点漂移要按拓扑检查
至少覆盖:
可以用 Pigsty/Ansible 采包事实:
具体 pig 子命令以目标版本为准。更稳妥的检查还包括:
对 control/library 取 hash,结果按 host 保存。不要只看 play recap:
它说明自动化执行状态,不说明数据库能加载文件。
漂移矩阵
| host | role | PG build | package build | control hash | library hash | preload live |
|---|---|---|---|---|---|---|
| pg-1 | primary | |||||
| pg-2 | replica | |||||
| pg-3 | replica |
任一 host 不同,先修供应层,不急着执行数据库 DDL。
14.5.2 声明安装与数据库内 CREATE EXTENSION
三个参数分别回答三个问题
含义:
| 参数 | 问题 |
|---|---|
pg_extensions |
cluster 节点要安装哪些扩展软件包 |
pg_libs |
server 启动时要 preload 哪些库 |
pg_databases[].extensions |
某数据库要创建哪些 SQL extension |
三者不能互换:
- 把
vector写进pg_extensions会把 SQL 名误作包别名; - 只写
pgvectorpackage 不会自动保证每个已有数据库都创建对象; - 把不需 preload 的库塞进
pg_libs会增加启动耦合; - 只执行
CREATE EXTENSION,备库节点可能仍缺包。
Pigsty 当前数据库声明示例:
这里用的是 SQL extension name。参见 Pigsty: Create Extensions。
本章声明片段
pigsty-declaration.example.yml 刻意写成:
它没有重复声明 pg_trgm,因为 stock Pigsty 默认已经在 public 启用。
本地直连实验为了让 namespace 与成员集中可见,把:
放在同一 schema。这是教学 fixture,不要求读者破坏 Pigsty 的合理默认。 平台实践可以是:
只要 ADR、查询、dump 与权限证据反映真实布局。
声明不应夹带对象版本假设
仓库安装“最新可用包”与数据库对象 VERSION 是两个控制面。Pigsty 初始化
声明能创建扩展,但生产需要单独的 SQL migration:
或:
是否在 Pigsty YAML 固定 version 要以该版本参数 schema 和初始化实现为
准;即使声明能写版本,已有数据库升级仍应通过有证据的迁移流程,而不是
假设重新跑初始化会更新。
推荐职责:
预加载变更的发布顺序
需要 preload 的扩展:
先 CREATE EXTENSION 再补 preload 可能直接失败;先改 preload 而节点缺库
可能导致 restart 失败。
本章两项扩展不要求 preload,因此声明不应为了“统一”加入它们。最小配置面 也是可靠性。
owner 与 schema
Pigsty 能创建角色、数据库与 schema;扩展 migration 仍要检查最终 owner:
对 untrusted extension,管理员创建后可能由高权限角色拥有。不要为了让 应用迁移工具“方便”而把 extension owner 交给 LOGIN 应用角色。
已有 cluster 的收敛
不要只编辑 inventory 后等下一次重建:
如果 playbook 只能在部分节点成功,停止数据库对象更新,先修节点一致性。
14.5.3 从监控和日志识别加载失败
一张三层检查表
供应层
数据库看到的 control:
若查不到,先看 server 实际 sharedir,不要先查 search_path。
进程层
再看每个实例启动日志:
配置声明、Patroni dynamic config、磁盘配置与 live setting 可能暂时不同。 以 live + restart history + log 为准。
数据库层
再查:
最后跑真正业务 probe。\dx 只证明 catalog 里有一行,不证明索引有效或
查询正确。
失败模式到动作
| 观察 | 解释 | 安全动作 |
|---|---|---|
pg_available_extensions 无记录 |
当前 server 看不到 control | 核对节点/PG major/安装目录 |
| available 有、installed 为空 | 包在,当前数据库未创建 | 走审批后的 CREATE EXTENSION |
| default > installed | 支持文件较新、对象仍旧 | 评审 update path,不自动升级 |
| installed 有、library 缺 | 节点漂移,failover 风险 | 阻断晋升,恢复 exact package |
pending_restart=true |
配置尚未生效 | 按 HA 策略滚动重启 |
| primary 成功、replica 加载失败 | 主备供应不一致 | 停止变更/晋升,修所有副本 |
must be owner |
生命周期权限边界生效 | 用受控 owner/admin migration |
must be superuser |
untrusted/control 要求 | 管理员评审,禁止给 app 提权 |
| object already exists | schema/历史手工对象冲突 | 盘点依赖,禁止 CASCADE 硬装 |
| no update path | package 脚本图不支持 | 选择受支持中间版本或迁移方案 |
监控哪些事实
低基数状态:
不要把每个 SQL 对象或 hash 都做成高基数时序标签。详细成员、文件 hash 和 包清单保存在 inventory/evidence;监控只暴露是否与期望一致,并链接 runbook。
事件/日志告警:
- postmaster 因库加载失败重启;
undefined symbol/ABI 错误;- extension update DDL 失败;
- recovery/replica 上 extension function 报错;
- extension 相关查询错误率突增;
- ANN/特殊索引 invalid;
- 更新后 P95/P99、WAL、内存、replica lag 越界。
L1 验证包
本章本地 evidence 明确写 pigsty_l1=not-run。在真实 L1 补齐:
每份 evidence 带:
凭证不得进入 evidence。
Pigsty 管理扩展的停止线
- inventory 别名无法解析到目标 OS/PG major;
- 本地仓库没有灾备/新节点需要的包;
- 任一 replica 包/hash 不一致;
- preload 变更未完成滚动重启;
- 只在
postgres数据库验证,业务数据库未盘点; - 默认
pg_trgm与自定义 schema 声明冲突; - playbook 成功但原生 catalog/query probe 失败;
- 没有 clean restore 与 major-upgrade 路线。
平台自动化可以缩短执行时间,不能降低验收标准。
本节结论
Pigsty 提供的是可声明、可重复的控制面:
PostgreSQL 提供的是 live 数据面事实:
两者一致,扩展才“可用”;再加升级、恢复和退出证据,才“可运营”。
上一节:生命周期与升级耦合 · 返回本章目录 · 下一节:建立可复用扩展 ADR · 查看全书目录 · 查看索引中心
14.6 建立可复用扩展 ADR
ADR(Architecture Decision Record)不是会议纪要,也不是给既定选择补理由。 它要让未来的维护者回答:
本章提供 扩展 ADR 模板。模板不是为了 填满十个标题,而是强迫“价值—运行—退出”形成闭环。
14.6.1 问题、候选、假设与成功标准
标题写问题,不先写扩展
较差:
更好:
第二种标题允许结论是:
- 采用 pgvector;
- 采用另一个 PostgreSQL 扩展;
- 使用外部服务;
- 使用精确检索;
- 现在不做。
候选没有绑架问题。
决策元数据
最小字段:
状态只用明确集合:
不要把 pilot 当没有期限的半批准。它必须有 traffic/data/environment 边界、
停止标准和截止复审日。
问题陈述
写:
示例:
每个数字链接到 query snapshot、dashboard 或数据集版本。没有证据的假设 单独列:
后续证据推翻假设时自动触发复审。
候选集合
至少包含:
- 不做;
- PostgreSQL 原生机制;
- 候选扩展;
- 外部服务/应用实现(若实际可行)。
对每个候选用同一维度:
| 维度 | 不做 | 原生 | 扩展 A | 外部服务 |
|---|---|---|---|---|
| 正确性/质量 | ||||
| P95/P99 | ||||
| 写入与资源成本 | ||||
| 一致性 | ||||
| HA/恢复 | ||||
| 升级/供应 | ||||
| 权限/安全 | ||||
| 退出成本 | ||||
| 团队技能/owner |
不要把“扩展一行 SQL”与“外部服务完整运维”比较;每格都是完整方案。
成功标准与停止标准成对出现
示例:
“比现在快”不是标准;“无明显问题”不是停止线。
分离硬门槛与权重
某些条件不可用加权分抵消:
否则一个非常快但不能恢复的扩展,可能用性能分“赢”过恢复硬门槛。
决策声明
结论写成:
这比“综合考虑后决定采用”更容易审计。
14.6.2 最小 PoC、风险清单与退出路径
PoC 的最小不是样本最少
最小 PoC 是覆盖决策最关键不确定性的最小实验。它不需要模拟所有生产流量, 但不能只跑 happy path。
扩展通用 PoC:
本章本地 PoC 覆盖其中 install、behavior、object update、dump 与文本出口; 没有覆盖 Pigsty L1、备库、clean restore 和 major upgrade,所以 vector 只能是 pilot。
fixture 必须确定性
记录:
- schema/data version;
- 生成方式;
- 随机 seed;
- 数据规模与分布;
- query 参数;
- expected rows/order/error;
- baseline checksum。
本章不是比较浮点的无限精度,而固定六位小数与 top ID:
对于近似索引,大数据 PoC 应定义 recall tolerance,而不是错误要求每次物理 计划与结果顺序完全相同。
正向、负向、破坏性测试分层
后两类必须在隔离 clone/L1 进行,有明确 target 与恢复路径。不要为了完成 ADR 在生产主库拔动态库。
本书 lab 的 destructive action 只接管:
并要求 marker、token、target 与无活跃 worker。生产迁移另写,不复用 “删掉重建”脚本。
evidence 不是终端滚屏
每轮输出一个不可变目录:
manifest 包含:
不要写密码、连接 URI secret 或生产个人数据。
风险清单有 owner 与触发器
| 风险 | 概率/影响 | 缓解 | 观测 | owner | trigger |
|---|---|---|---|---|---|
| package 在新 PG major 缺失 | 提前构建/替代 | release matrix | major roadmap | ||
| C library crash | canary/rollback | crash/restart | error budget | ||
| ANN recall 漂移 | golden corpus | quality job | model/data change | ||
| restore 缺旧脚本 | repo snapshot | restore drill | retention review | ||
| vendor/license 改变 | legal/exit | periodic review | new terms | ||
| node package drift | Pigsty convergence | parity probe | failover/new node |
没有 owner 的风险不是被管理,只是被记录。
退出路径从依赖图开始
还要查引用扩展成员的业务对象。退出步骤必须显式:
最后一步不是第一步。包删除前要考虑历史备份与降级节点。
验证退出,而不是只验证导出
退出 PoC 成功条件:
- 导出行数/主键/checksum 匹配;
- 目标表示能承载单位、坐标系、模型与精度;
- 新查询结果和 SLO 在容差内;
- 旧应用与新 schema 的兼容窗口成立;
- 无残余 view/function/index/table 依赖;
DROP EXTENSION在不使用CASCADE时成功;- 包与 preload 清理后实例重启、备库和恢复通过。
14.6.3 结论的版本范围和复审触发器
ADR 是带范围的结论
错误:
可执行:
范围外不是自动拒绝,但必须重新验证。
版本块
“支持 PG14–18”可以是项目宣称;ADR 的验证范围可能只完成 17/18。两者分列。
复审触发器
日历触发:
变更触发:
- PostgreSQL major/minor 或内核供应者变化;
- Pigsty release、OS、CPU architecture 变化;
- extension project/package/object version 变化;
- control 的
trusted/preload/requires/relocatable变化; - 数据规模、分布、语言、模型、维度、距离度量变化;
- 新建/替换 standby、灾备或恢复镜像;
- SLO、错误预算或容量越界;
- crash、错误结果、安全通告;
- 维护者、许可证、供应商或仓库变化;
- clean restore/upgrade drill 失败;
- 退出成本估算越过窗口。
不覆盖历史,使用 supersede
决策改变时:
保留旧 ADR:
- 能解释旧备份/旧服务为何依赖它;
- 能追踪当时证据;
- 能区分错误决策与条件变化;
- 能为事故和退出提供历史。
只在原文底部改“现在改用 Z”,会抹掉因果链。
把复审接入变更门禁
自动检查:
若任一发生:
不要让监控自动决定架构,但让它阻止“版本已经漂了,ADR 仍显示已批准”。
供第 15–17 章复用
后续三章沿用同一模板,但各自增加领域项:
第 15 章检索
第 16 章时空
第 17 章分析与分布式
它们可以增加字段,不能删掉供应、恢复、权限和退出。
ADR 验收问题
评审者逐句问:
- 问题是否在没有候选扩展名时仍成立?
- 是否有“不做”和原生替代?
- 成功/停止标准是否能机器或人工复验?
- 是否写了 exact server/package/object 版本?
- 是否测过未授权失败?
- 是否覆盖备库、clean restore 和 major upgrade?
- 自定义数据能否导出,退出是否不用
CASCADE? - 残余风险是否有 owner?
- 哪个变化会让结论失效?
- evidence 能否由另一位工程师重跑?
任一回答“以后再补”,ADR 状态最多是 proposed/pilot。
本节结论
好的扩展 ADR 不是“为什么喜欢它”,而是一个可撤销承诺:
它让采用扩展成为受控工程选择,而不是永久信仰。
上一节:用 Pigsty 管理扩展可用性 · 返回本章目录 · 下一节:实战:评审三个候选扩展 · 查看全书目录 · 查看索引中心
14.7 实战:评审三个候选扩展
本节把前六节压成一个 release proposal:
它不是扩展性能评测,也不是生产安装脚本。实验的价值是证明评审结构能够 运行、失败、复位和重复。
环境与破坏边界
正式 evidence 来自 Homebrew PostgreSQL 18.6 直连服务,未在 Pigsty L1 运行。
task.sh all会精确删除并重建带本章 marker 的shop_ch14、pg_trgm和vector,只适合本地/开发数据库。生产变更不得运行这一 “删后重建”入口。
14.7.1 一个接受、一个试点、一个拒绝
问题 A:有界单字段拼写容错
候选 pg_trgm。
问题边界:
原生替代:
- 精确 B-tree;
- 规范化前缀搜索;
- PostgreSQL FTS;
- 应用侧拼写纠正。
采用理由:
- PostgreSQL contrib 扩展;
- 当前 control 为 trusted/relocatable;
- 不改变
title text类型; - GIN
gin_trgm_ops可由目录与计划验证; - 可以先切回精确/FTS,再删 GIN 和扩展;
- 1.3 → 1.6 更新路径与行为回归可重复。
结论:
不是批准它替代第 15 章的全部检索设计。
问题 B:语义近邻检索
候选 vector(项目/包别名常为 pgvector)。
本地 PoC:
已证明:
- control/安装 SQL/动态库存在并有 hash;
trusted=false,非超级用户创建以42501失败;- 管理员能在目标 schema 创建 0.8.4;
- 表、类型、HNSW opclass/index 有目录证据;
- 应用角色可查询但没有表写权限;
embedding::text可导出五行;- 全库 dump 用
CREATE EXTENSION vector表示成员。
未证明:
- 真实 embedding model、dimension 与 normalization;
- 真实语料 relevance/recall;
- 过滤组合下 ANN 行为;
- 索引 build、内存、磁盘、WAL 与更新成本;
- 并发 P95/P99;
- 物理备库/failover;
- clean restore;
- PostgreSQL major upgrade;
- 在 Pigsty L1 所有节点的包一致性。
结论:
任何真实业务接入前必须补上第 15 章的质量语料与 L1 生命周期证据。
问题 C:分布式分片
候选 Citus。
当前事实:
本地 Homebrew server 的 pg_available_extensions 也没有 Citus,但这不是
拒绝的主要理由。Pigsty 当前扩展目录提供 Citus 14.0.0;平台有包仍不能替
架构证明问题。
当前替代:
- 修正查询与索引;
- 生命周期/归档治理;
- PostgreSQL declarative partitioning;
- 垂直扩容;
- 读副本或分析副本;
- 到第 17 章测量单集群容量边界。
重新打开 ADR 的条件:
结论:
拒绝的是当前采用时机,不是产品评价。
把结论写进数据库
setup.sql 建立:
最终必须精确得到:
把 ADR 行放进实验数据库不是建议生产数据库存文档;它使 fixture checksum 同时覆盖数据与决策,防止测试脚本与文字结论分叉。
最小架构
所有 schema、扩展与非成员 relation/index 带 marker:
扩展成员通过 pg_depend.deptype='e' 识别,不要求逐个添加 comment。
14.7.2 在 L1 安装并验证原生对象与平台状态
标题中的 L1 是目标运行形态,不是本地证据伪装。流程分两步:
- 在受控直连 PostgreSQL 完成机制 fixture;
- 把同一合同移植到 Pigsty L1,补齐节点、HA 与恢复证据。
1. 准备 libpq service
service 文件权限收窄,不在命令行或 evidence 打印密码。
context guard 要求:
版本不符时脚本拒绝;读者应复制 proposal、更新版本与 golden 后重新评审, 不应删掉 guard。
2. 分阶段入口
每个会精确重建 fixture,适合单独教学。最终只认:
3. 先验证支持文件
package-manifest.txt 记录:
并对:
生成 SHA-256。
脚本先比较 pg_config major 与 live server major。PATH 指向错误 PG 安装时
立即失败,不会拿另一套支持文件做出“可用”结论。
在 Pigsty L1,这份清单要按所有主备 host 展开,而不是只在 primary 生成。
4. 碰撞保护与 trusted 安装
setup.sql 若发现:
shop_ch14marker/owner 不符;pg_trgm或vector已位于别的 schema;- extension marker、owner 或版本不在允许集合;
- schema 中有未知非 extension relation/routine/type/operator/opclass;
就拒绝重建。
随后:
结果:
这证明 trusted 规则与数据库 owner 权限,不表示 pg36_owner 是超级用户。
5. 注入预期特权失败
必须:
若它意外成功,说明 control/权限环境与 proposal 不同,review 失败,而不是 把差异忽略。
管理员再执行 install-vector.sql:
并由 owner 建表:
6. 建立两个可验证索引
目录验收:
| index | AM | opclass | valid/ready/live |
|---|---|---|---|
candidate_doc_title_trgm_idx |
gin |
shop_ch14.gin_trgm_ops |
true/true/true |
candidate_doc_embedding_hnsw_idx |
hnsw |
shop_ch14.vector_l2_ops |
true/true/true |
CREATE INDEX 成功还不够;检查 pg_index、pg_am 与 pg_opclass,防止
名字相同但实现漂移。
7. 采集扩展与成员目录
更新前 PostgreSQL 18.6:
member-catalog.sql 再按 catalog 分解:
更新后 pg_trgm 成员为 47。数量只冻结本次 PG18.6 build;其他 major 可有
条件差异。
8. 权限矩阵
pg36_app:
app-query.sql 成功使用函数、操作符与类型;随后:
必须:
应用使用能力与扩展管理权被分离。
9. 行为 baseline
模糊检索:
结果:
向量检索:
结果:
10. 索引计划
五行表优化器自然可能选择 seq scan。实验:
只用于证明索引路径存在,不用于性能结论。
trigram:
vector:
生产验收应恢复默认 planner 配置,用真实数据比较:
并检查结果质量;不能用 enable_seqscan=off 证明索引值得使用。
11. 更新 1.3 → 1.6
update-paths.sql 先证明:
upgrade.sql 要求 source 精确为 1.3:
若 source 已经变化,返回本章自定义 P3640,不猜迁移路径。
更新后重新采集:
- available version installed flag;
- extension/member catalog;
- index validity/opclass;
- ACL;
- 两个查询;
- 两个计划。
除 pg_trgm_version 与成员清单外,行为 golden 不变。
12. dump 的正反例
全库:
必须包含:
且不展开 shop_ch14 扩展成员函数/类型。
选择性 schema:
它包含应用表和索引,却没有 CREATE EXTENSION。review 把这个“不完整依赖”
作为预期证据,提醒恢复 runbook 先供应并创建扩展。
13. portable exit
review 要求:
- header 精确;
- 五个主键按 1..5;
- 每个 embedding 是 bracketed text。
生产退出还需导入目标、语义比对和删依赖;本章只证明可携带 representation。
14. 最终不变量
checksum 包含:
- 三行 ADR 内容;
- 五行文档/向量文本;
- extension name/version/schema/relocatable。
它不包含管理员用户名,避免换一个受控超级用户就改变业务 golden。
15. 精确复位
手工入口:
reset.sql 在删除前验证:
- database、writable instance、server 与角色;
- token/target;
- schema marker/owner;
- extension name/version/schema/owner/marker;
- 非成员 relation/type/routine/operator/opclass 白名单;
- 没有
pg36-ch14-*活跃 worker。
删除顺序:
没有 CASCADE。若仍有未知业务依赖,DROP EXTENSION 失败并暴露它。
all 还注入:
拒绝后才精确复位,再完整重建第二遍。最终环境保留通过验收的 fixture。
16. 移植到 Pigsty L1
先审查 Pigsty 声明片段:
stock Pigsty 默认把 pg_trgm 启用在 public,无需与本地 shop_ch14 布局
完全相同。
L1 执行顺序:
L1 不应强行复用本地 proposal checksum,因为:
- schema 布局可能不同;
- package build/OS 不同;
- owner 名或 default extension state 不同;
- 应补主备/restore evidence。
复制 ADR 结构,生成属于目标 L1 的新 baseline。
14.7.3 产出供 ch15–ch17 复用的 ADR 模板
交付包
本章交付不是一张“推荐扩展”表,而是:
candidate-review.md 记录三项结论; extension-adr-template.md 提供十段结构:
- 决策元数据;
- 问题与边界;
- 候选与原生替代;
- 成功与停止标准;
- 供应链与运行条件;
- 数据与兼容性;
- 安全与治理;
- 最小 PoC;
- 退出路径;
- 结论。
第 15 章:检索候选如何复用
继承通用字段,再增加:
pg_trgm 的 accept 不能自动批准所有字段。每个字段/查询形态仍需索引与
相关性 ADR。
vector 的 pilot 进入第 15 章后,要补:
第 16 章:时空候选如何复用
增加:
PostGIS 若被采用,自定义类型的 restore/exit 门槛不能因为生态成熟而省略。
第 17 章:分析与分布式候选如何复用
增加:
Citus 只有在这些字段有证据后才从 reject 重新进入 proposed;“Pigsty 有包” 不是触发批准。
自动审校器检查什么
review.py 不比较终端输出的外观,而比较关系:
关系式 review 比“命令 exit 0”更接近发布验收。
审校结果
正式两轮输出:
第一轮通过后,脚本证明复位 guard,再删除并重建,第二轮得到同一关系和 proposal identity。
哪些结论可以带走
可以:
- 扩展要同时管理供应、进程和数据库三层;
- trusted/untrusted 与 owner 边界必须负面测试;
- package version 与 extversion 分开;
- update 前后比较 catalog、行为和计划;
- dump 不携带支持文件,选择性 dump 不保证依赖闭包;
- 自定义类型采用前先定义交换格式;
- Pigsty 声明后回到原生证据;
- ADR 允许 accept/pilot/reject,而不是所有候选二选一。
不能:
pg_trgm对所有搜索都足够;- pgvector 0.8.4 已通过生产验证;
- Citus 不值得使用;
- PostgreSQL 14–17 会得到相同成员数;
- Homebrew 文件 hash 能代表 Pigsty 包;
- 五行查询速度能代表生产性能。
能清楚说出“实验没有证明什么”,是扩展治理成熟度的一部分。
本章最终检查
完成本章后,面对新扩展先写:
然后才写:
顺序反过来,数据库很快会积累一组谁也不敢升级、恢复或删除的隐性平台。
上一节:建立可复用扩展 ADR · 返回本章目录 · 下一章:见微知著:全文、模糊与向量检索 · 查看全书目录 · 查看索引中心