跳至内容
14.3 扩展选型的六个问题

14.3 扩展选型的六个问题

扩展评审最容易从产品介绍开始:

它支持什么?

更好的起点是:

我们已经观察到什么问题?

本节用六个问题形成漏斗:

  1. 具体问题和原生替代是什么?
  2. 成功、停止与反例怎样测量?
  3. 是否引入数据格式锁定,怎样导出退出?
  4. 备份、复制与升级生命周期是否成立?
  5. 维护、许可证与商业连续性如何?
  6. 权限、崩溃面和供应链风险能否接受?

前两问证明价值,中间两问证明可运营,后两问证明风险归属。任何一问没有 答案,都只能进入调查或限域试点,不能直接成为平台默认。

14.3.1 它解决的具体问题和原生替代是什么

问题必须可证伪

以下不是问题陈述:

我们需要向量数据库。
我们需要分布式 PostgreSQL。
大家都在用时序扩展。
这个扩展会让查询更快。

它们已经把候选解写进需求。可评审的陈述应包含:

workload + current evidence + target + boundary

例如:

商品标题查询中,8% 的零结果请求只有一个拉丁字母拼写错误;
在 2000 万活跃标题、P95 50 ms 的边界内,希望返回至多 20 个候选;
中文分词、语义搜索和全站文档检索不在本次范围。

这时 pg_trgm 才是候选之一,而不是需求本身。

先列 PostgreSQL 原生替代

“原生”不等于永远更好,但它通常具有更小供应面。按问题检查:

问题先检查
精确/前缀查找B-tree、表达式/partial index、规范化列
词项全文检索tsvector、GIN、词典与查询函数
范围/包含/重叠range/multirange、GiST、EXCLUDE
半结构化属性jsonb + GIN/表达式索引,或重新建模
地理点/简单距离内置 point 是否真的足够;复杂 GIS 再评 PostGIS
时间分区/归档declarative partitioning、维护流程
容量问题查询/索引修正、归档、分区、纵向扩容
批量分析物化、并行查询、专用副本或外部分析系统

还要列应用/外部服务替代。一个扩展减少网络跳数,但把失败和升级绑定到 PostgreSQL;外部服务增加分布式复杂性,却可能提供独立扩缩容与专用算法。 这是工程权衡,不是“数据库内一定更快”。

比较单位是完整方案

不要比较:

one SQL function vs one HTTP call

而比较:

PostgreSQL extension solution
  package + preload + schema + index + backup + standby + upgrade + skills

external service solution
  service + network + sync pipeline + consistency + backup + operations

native solution
  schema/query/index + application behavior + operational limits

遗漏生命周期成本,会让扩展看起来永远最简单;遗漏外部同步成本,又会让 独立服务看起来永远更可扩展。

第二问:成功与停止怎样测

一个 PoC 至少同时有:

  • 正确性/质量:结果集合、不变量、召回/精度或误差;
  • 性能:P50/P95/P99、吞吐、build time、写放大;
  • 资源:内存、磁盘、CPU、WAL、临时文件;
  • 运行:备库延迟、恢复时间、升级锁、失败表现;
  • 边界:数据规模、过滤选择性、并发、语言/模型/维度;
  • 停止线:何时立即拒绝或回到替代方案。

本章五行 fixture 的成功标准故意很窄:

pg_trgm: top ids 1,5,2 and GIN plan is usable
vector:  top ids 1,2,5 and HNSW plan is usable

它证明 API 与索引机制,不证明:

真实搜索质量
大规模 ANN recall
生产尾延迟
写入与索引构建成本
备库和恢复 SLO

因此 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 让:

embedding shop_ch14.vector(3)

成为列类型。只要该列存在:

DROP EXTENSION vector;

就会因依赖失败;使用 CASCADE 会把业务对象一起删除,不是可接受的退出。

在采用前写出出口

本章先建立可移植导出:

COPY (
    SELECT
        doc_id,
        title,
        embedding::text AS embedding_text
    FROM shop_ch14.candidate_doc
    ORDER BY doc_id
) TO STDOUT WITH (FORMAT csv, HEADER true);

输出形如:

1,PostgreSQL extension guide,"[1,0,0]"

这只建立一个交换入口。真正退出还要回答:

  • 文本格式由谁解析,精度是否损失;
  • 行数、主键和 checksum 怎样核对;
  • 目标类型是什么;
  • 应用何时双读/双写;
  • ANN 索引何时停止使用;
  • 大表转换是否重写、锁多久、产生多少 WAL;
  • rollback 点在哪里;
  • 备份中最后一个扩展依赖何时消失。

没有跑过迁移的“理论可导出”只能算风险缓解,不能算完成退出演练。

不把逻辑 dump 当数据出口

全库 pg_dump 通常写:

CREATE EXTENSION IF NOT EXISTS vector WITH SCHEMA shop_ch14;

这仍要求恢复端安装 vector。它是 同构恢复 合同,不是脱离扩展的出口。

真正的 portability artifact 应使用目标系统可理解的格式:

  • 内置 text/numeric/jsonb/array
  • CSV/JSON/Parquet 等交换格式;
  • 明确坐标系、单位、模型与版本的领域格式;
  • 行数、范围和 checksum。

例如 PostGIS 几何不能只导出一个没有 SRID 的坐标字符串;embedding 不能 只导出数字而丢掉 model、dimension、normalization 与 distance metric。

第四问:生命周期是否成立

锁定不只发生在数据格式,也发生在运维路径。逐项问:

install:
  every primary/standby/restore host?

backup:
  pg_dump and physical backup prerequisites?

restore:
  clean environment package bootstrap order?

replication:
  physical library parity?
  logical subscriber type/schema parity?

upgrade:
  extension object update path?
  server-major-compatible binary?
  lock and downtime?

rollback:
  package rollback?
  object downgrade script?
  data format backward compatibility?

只有 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”也可能是成熟稳定。 用与你的风险相关的证据判断。

许可证检查三个层次

至少分别检查:

  1. 源码许可证;
  2. 二进制包与捆绑依赖;
  3. 企业使用、再分发、托管服务或商业功能条款。

不要从项目名称、GitHub 页面徽章或旧博客推断。保存目标版本的 LICENSE、 NOTICE、依赖清单与法务结论。许可证可能随 major、模块或商业发行版变化。

技术上能装,不表示组织有权按计划分发;开源,也不表示所有附加服务和 品牌条款相同。

商业连续性不是“有公司背书”

公司支持可以降低某些风险,也引入:

  • 定价或授权变化;
  • 产品方向与开源版分叉;
  • 单一 vendor build;
  • 支持合同终止;
  • 收购、停服或仓库下线。

社区项目则可能有 bus factor、发布带宽和联合支持问题。两者都要问:

如果主要供应者明天停止交付,
我们能否合法获得源码、复现构建、修补安全问题、
恢复历史备份并迁出数据?

答案不一定要求团队自己维护 fork,但必须有时间与责任人。

建立维护快照

ADR 中保存带日期的证据:

project_release: exact-tag
reviewed_at: 2026-07-29
supported_pg: [14, 15, 16, 17, 18]
target_build: exact-package
license_review: ticket-or-document
security_contact: ...
last_restore_test: ...
next_review: ...

维护状态会变化,所以结论必须有复审日,不能把一次评估写成永久事实。

14.3.4 权限、崩溃面与供应链风险

第六问:谁获得什么能力

扩展评审画出特权链:

repository maintainer
  -> package builder
  -> node installer (root)
  -> PostgreSQL admin
  -> extension owner
  -> schema/object owner
  -> application users

每一环都可能改变下一环执行的代码。要记录:

  • 谁能把包加入仓库;
  • 谁能改 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、超大查询和对象劫持风险。

供应链不止校验下载文件

最小证据链:

upstream source/tag
  -> trusted build pipeline
  -> signed repository metadata
  -> exact OS package
  -> control/SQL/library hashes on nodes
  -> database extversion/member inventory

本章 package manifest 对这些具体文件做 SHA-256:

pg_trgm.control
pg_trgm--1.3.sql
pg_trgm--1.3--1.4.sql
pg_trgm--1.4--1.5.sql
pg_trgm--1.5--1.6.sql
pg_trgm shared library
vector.control
vector--0.8.4.sql
vector shared library

哈希能发现漂移,不能证明代码安全。它必须与仓库签名、构建来源、审计和漏洞 响应组合。

风险分级

一个实用起点:

等级例子最低门槛
L0 可重建 SQL/索引不改变持久类型、无 preload功能/计划/恢复/退出
L1 自定义类型/C 函数持久列依赖动态库供应锁、主备、clean restore、出口
L2 preload/hook/workerserver 启动与全局执行路径滚动重启、crash/failover、资源与禁用
L3 存储/分布式拓扑WAL、shard、专用 catalog完整故障模型、升级/回退、联合支持

等级不是产品好坏,而是证据成本。高等级候选可以采用,但不能用 L0 的 “创建成功”验收。

决策状态

只允许清晰状态:

investigate  资料不足
pilot        限域、有停止线、不得成为默认依赖
accept       在明确版本/场景内批准
reject       当前问题或风险不匹配
superseded   已由新 ADR 替代

避免“原则同意”“先装上再看”“有需要都可以用”这类无法执行的结论。

本节结论

六问的顺序很重要:

problem -> evidence -> data/exit -> lifecycle -> continuity -> security

越早失败,越应尽早停止。没有实际问题时,不需要花几周证明供应链;问题 成立后,也不能用性能收益跳过恢复和退出。


上一节:内核、发行版与托管服务 · 返回本章目录 · 下一节:生命周期与升级耦合 · 查看全书目录 · 查看索引中心

最后更新于