18.1 从数据库产品到能力组合
当团队说“我们用 PostgreSQL”时,这句话通常混合了三种不同事实:
product
一个特定版本的 PostgreSQL server
capabilities
事务、约束、检索、时空、分析、复制、恢复……
service
带身份、入口、目标、配额、支持和生命周期的对外交付产品可以启动,不代表所有能力可用;能力可以运行,不代表服务可承诺。平台 设计的第一步,是把这三层重新拆开。
18.1.1 事务、检索、时空、分析与任务能力
从业务问题开始,而不是从组件清单开始
pg36_shop 至少需要处理以下业务问题:
| 业务问题 | 需要的能力 | 首选正确性边界 |
|---|---|---|
| 订单与库存能否保持不变量 | 关系约束、事务、并发控制 | PostgreSQL commit |
| 应用如何安全调用数据能力 | 角色、schema、prepared SQL、API 合同 | DB + 应用 |
| 商品如何被关键词找到 | 全文/模糊检索、排序、质量 golden | PostgreSQL 起步 |
| 商品如何按语义相似找到 | embedding、向量距离、ANN 质量 | 试点 |
| 配送事件在哪里、何时有效 | 空间、时间、边界和参考系 | PostgreSQL 起步 |
| 月报如何快速且不过度影响交易 | 并行、索引、汇总、离线读 | PostgreSQL + 隔离 |
| 订单事件如何送到其他服务 | outbox、relay、消息投递 | 跨系统合同 |
| 图片字节放在哪里 | 对象存储与元数据协调 | 按数据域拆分 |
先写能力,能避免两种常见偷换:
“PostgreSQL 支持” -> “我们的服务已经支持”
“引入了某产品” -> “业务问题已经解决”例如,vector 扩展已经在第 14 章的开发 fixture 中安装,<=> 距离查询也在
第 15 章得到正确结果。这证明“语义检索试验可运行”,没有证明:
- embedding 模型会被稳定版本化;
- ANN 在生产规模下满足质量与延迟;
- 索引重建落在维护窗口;
- 副本、备份、恢复和升级路径全部可用;
- 团队可以在模型漂移时诊断结果变化。
所以它的生命周期是 pilot,不是 accepted。
事务能力是权威状态的锚
对订单、库存和支付,最重要的不是“能存 JSON”或“QPS 很高”,而是所有 写入者看到同一套不可违反的事实:
order total = sum(order item totals)
payment currency = order currency
inventory cannot be consumed below the approved boundary
status transition belongs to a finite allowed graph
duplicate request does not create a second business effect第 4、10、13 章分别从约束、并发与数据库逻辑证明了这些能力。只要这类状态 仍由 PostgreSQL 定夺,缓存、搜索索引、事件流和湖仓都只能是派生物。
“source of truth” 容易变成口号,更精确的写法是按数据域声明 authority:
product business state -> PostgreSQL
cache entry bytes -> cache
order publication intent -> PostgreSQL outbox
message delivery/replay log -> event bus
media object bytes -> object storage
media identity/state/checksum -> PostgreSQL
analytical projection -> lakehouse
operational business state -> PostgreSQL冲突时相信谁,必须在发生冲突前写清楚。
检索是一组能力,不是一条 LIKE
商品检索至少可以分成:
exact identity lookup
prefix / substring
typo-tolerant fuzzy match
lexical relevance
phrase / field weighting
semantic similarity
filter + rank
faceting / aggregation
highlighting
freshness and deletion第 15 章的质量 golden 证明 pg_trgm 适合当前模糊检索基线,vector 适合
受控 pilot。它没有把“搜索”宣布为永久留在 PostgreSQL。蓝图保存一个明确
触发器:
当质量、规模、语言能力或独立可用性目标击败 PostgreSQL 基线时,才启用
external-search-projection-v1。
这样,外部检索是一个由证据触发、可重建、可退出的投影,而不是架构图里一 开始就存在的时髦方框。
时空能力先统一语义,再比较引擎
空间系统最危险的错误往往不是查询慢,而是答案看起来合理:
SRID 混用
经纬度顺序颠倒
边界包含规则不一致
event time 与 ingest time 混淆
本地时区被当 UTC
有效时间区间重叠
迟到事件被静默丢弃第 16 章把 EPSG:4326、UTC、区间与边界规则固化为 fixture。PostGIS 让空间 类型、操作符与索引进入 SQL,但业务合同仍然高于扩展:迁移到其他引擎时, 这些语义必须不变。
因此 capability 的结构至少要包含:
{
"placement": "postgresql",
"system_of_record": "postgresql",
"state": "conditional",
"consistency": "UTC + versioned validity + EPSG:4326",
"freshness": "event ingest plus measured lateness",
"externalization_trigger": "retention or throughput misses objective"
}分析能力先减少工作,再横向扩展
第 17 章已经建立顺序:
正确性 golden
-> 计划与统计
-> 索引 / BRIN
-> 并行
-> spill 证据
-> 汇总
-> OLTP/OLAP 隔离
-> 分布式门槛当前蓝图选择“本地汇总 + offline replica”作为第一步,而不是选择 Citus。 这不是永久拒绝分布式;第 26 章的容量证据可以触发新 ADR。
同理,postgres_fdw 的 loopback fixture 只证明过滤、聚合和部分失败的
查询形状。PostgreSQL 官方
Foreign Data
说明外表通过 wrapper 访问外部数据,并通过 user mapping 提供认证信息。
本书实验使用的 password_required=false 没有生产身份、网络与故障域,
所以严格标为 lab-only。
任务能力要分事务内与事务外
“数据库里能执行函数”不等于“任何业务任务都应放进事务”。一个实用边界:
留在事务内:
约束检查
小而有界的派生值
幂等状态转换
短路径的 outbox 写入
与当前事务必须原子完成的审计事实移到事务外:
HTTP / RPC
发送邮件或短信
大批量重算
不确定时长的模型推理
跨系统补偿流程
无限或长期重试事务内代码失败可以回滚;远端世界通常不能跟着 PostgreSQL 回滚。把两者硬塞 在一起,会产生持锁时间、连接占用、重试重复和不可控级联。
能力清单必须有状态
本章采用以下生命周期词汇:
| 状态 | 含义 |
|---|---|
accepted | 已有当前范围证据,仍受版本与服务门约束 |
accepted-with-scope | 只在明确狭窄边界内接受 |
accepted-first-step | 当前首选,但容量触发器仍待观察 |
conditional | 前置门尚未全部通过 |
pilot | 只允许受控试验,不进入默认生产服务 |
lab-only | 教学机制证据,禁止生产准入 |
deferred | 触发条件未出现,不增加系统 |
没有状态的能力清单会把“听说过”“装得上”“试过一次”和“可值班”放在同一 列,最终无法治理。
18.1.2 计算、存储、接入、控制与观察平面
能力回答“要做什么”,平面回答“由哪类机制完成”。
数据与计算平面
数据/计算平面直接处理请求与业务状态:
PostgreSQL backend
tables / indexes / WAL-visible changes
SQL planner and executor
constraints / functions / triggers
extension types and operators
replica read execution
external projection workers它的典型证据是:
- SQL 结果和业务校验和;
- 执行计划与实际行数;
- 事务、锁与 SQLSTATE;
- replica replay 位置;
- 投影 watermark。
在这个平面上,“成功”意味着某次业务操作按合同完成,不代表平台整体健康。
存储平面
存储平面不仅是 PGDATA:
| 存储 | 保存什么 | 关键风险 |
|---|---|---|
| PostgreSQL data files | heap、index、catalog | 损坏、容量、延迟 |
| WAL | 恢复与复制所需变化 | 归档缺口、保留爆炸 |
| backup repository | base/diff/incr 与 WAL | 不可恢复、凭据、同域失效 |
| temp | sort/hash 等中间结果 | 磁盘耗尽、I/O 争用 |
| object storage | 媒体或备份对象 | 清单、版本、删除 |
| cache | 可丢弃派生数据 | 陈旧、穿透、错误权威 |
| analytical/search index | 可重建投影 | lag、语义漂移、删除遗漏 |
“数据有三份”不是恢复策略。三份流复制副本会复制同一条误删;三个位于同一 存储故障域的节点也不是三个独立副本。第 21、32、35 章会分别验证备份恢复、 PITR 与损坏取证。
接入平面
接入平面把服务语义变成客户端可连接的端点:
name / VIP / address
port
TLS and authentication
pooling mode
target selector
health check
failover routing
connection and queue budget
session-state contract一个主库 IP 只是位置,不是服务。客户端需要知道:
这个入口是否可写
是否允许陈旧读
是否 read-your-writes
发生切换时连接怎样断开
事务池能否使用 session feature
取消与超时如何传播Pigsty 的
Service Access
用端口和 selector 表达 primary、replica、default、offline 等服务。
第 22 章会把这些入口与 PgBouncer 语义、连接预算一起验收。
控制平面
控制平面改变系统的期望状态:
inventory and configuration
package and extension versions
cluster membership
leader election
switchover/failover decision
backup schedule and retention
role / database provisioning
change rollout and rollback控制平面故障与数据平面故障不同。数据库可以继续处理流量,而 inventory 已经 漂移;也可能数据库本身健康,但错误的健康检查把流量切走。
控制平面必须回答:
- 谁能改;
- 改什么对象;
- 如何审阅;
- 是否幂等;
- 如何观察收敛;
- 哪一步是最后可逆点;
- 失败时由谁接管。
Ansible 成功退出只说明某次自动化执行没有报告失败;它不是业务 SLO 证据。
观察平面
观察平面收集并解释:
metrics
logs
traces
catalog snapshots
query fingerprints and plans
backup manifests
configuration drift
synthetic probes
business invariants观察平面不应只回答“图绿不绿”,还要让值班者完成因果链:
用户症状
-> 受影响的服务入口
-> 当前拓扑与流量目标
-> 数据库等待/资源/复制/恢复状态
-> 最近变更
-> 可逆且风险最小的动作第 25 章会验证信号到 runbook 的连接。本章只列出必须覆盖的八类信号,不声称 告警已经有效。
管理平面与业务平面不要共用身份
一个常见反模式:
application connection
= object owner
= extension installer
= backup operator
= cluster administrator本书 fixture 已把角色分开:
| 身份 | 当前边界 |
|---|---|
pg36_app | LOGIN、非 superuser、运行时 |
pg36_owner | NOLOGIN、对象所有者 |
postgres | 本地正式实验管理员 |
这还不是完整生产角色模型。第 23 章要继续拆出迁移、监控、备份、复制、审计 与应急身份。
一项能力会穿过多个平面
以“商品模糊搜索”为例:
data/compute pg_trgm operator + GIN/GiST plan
storage product table, index, WAL, backup
access read endpoint, role, timeout
control extension package/version, CREATE EXTENSION, schema
observe latency, quality golden, index build, bloat, errors
governance owner, lifecycle, upgrade and exit只验证 SQL 正确,会漏掉四个平面;只部署组件,则连第一项也未必正确。
18.1.3 组件组合必须有统一服务目标
局部健康不推出整体健康
假设请求路径是:
client
-> DNS/VIP
-> HAProxy
-> PgBouncer
-> PostgreSQL primary
-> transaction
-> outbox
-> relay
-> event bus
-> consumer每个组件都有自己的“up”,但业务关心的是:
订单是否只创建一次
提交后多久可查询
事件多久送达
失败后是否可安全重试
是否会丢、重、乱序
能否在目标时间内恢复如果数据库 20ms 提交、relay 卡 40 分钟,订单 API 的数据库延迟指标仍然很 漂亮,业务事件服务却已经违约。
先定义服务,再分配组件目标
一个服务目标模板:
| 维度 | 示例 |
|---|---|
| 用户动作 | 创建订单 |
| 成功定义 | 返回稳定 order_id,状态可读,重复请求无第二次效果 |
| 延迟 | API P95/P99 |
| 正确性 | 约束、金额、状态、幂等全部成立 |
| 可用性 | 测量窗口、排除项、错误预算 |
| 新鲜度 | 提交后 read-your-writes;事件 publish lag |
| 持久性 | 故障模型内的 RPO |
| 恢复 | 场景化 RTO,不只“启动成功” |
| 容量 | 峰值并发、增长与余量 |
| 安全 | 身份、租户、数据分类 |
| 成本 | 服务单位成本与预算 |
组件指标从这个目标推导。不要反过来因为某仪表盘有一个指标,就把它升格为 服务 SLI。
可用性相乘只是一种初步直觉
若一条同步路径必须经过多个独立组件,在非常简化的独立假设下:
[ A_{path} = \prod_{i=1}^{n} A_i ]
三个各 99.9% 的串行依赖约为:
[ 0.999^3 \approx 99.7003% ]
这提醒我们“组件都三个九”并不保证路径三个九。但不能把这个公式当精确生产 模型,因为:
- 故障往往相关,共享网络/电源/配置/身份;
- 重试、缓存和降级会改变路径;
- 读写操作依赖不同;
- 维护与区域故障不是独立伯努利事件;
- 业务正确性失败可能不表现为组件 down。
真正的目标要通过故障模型、演练和真实 SLI 验证。
新鲜度预算也会叠加
派生投影可能经历:
transaction commit
-> outbox polling
-> broker publish
-> consumer queue
-> indexing
-> alias visibility总 lag 不是只看 broker lag。每一段要有时间戳或位置:
| 阶段 | 证据 |
|---|---|
| source commit | commit LSN / business version |
| publication intent | outbox timestamp |
| broker | partition/offset |
| consumer | acknowledged source identity |
| projection | applied version / watermark |
| query | served generation |
没有共同 identity,端到端新鲜度无法对账。
正确性优先于可用性包装
“失败时返回旧缓存”可能提高响应可用性,也可能把已取消订单重新显示为有效。 降级必须按数据和操作分类:
public product description
可允许有标签的短时陈旧
inventory availability
陈旧读可能导致超卖,不能默认降级
payment state
不能用缓存猜测
analytics dashboard
可显示 last complete watermark同一个缓存组件不能用一个统一 fallback 策略覆盖所有字段。
SLO 目标必须与证据状态绑定
本章目录中的:
"availability_target": "99.9%-proposal"故意带有 -proposal。要去掉它,至少要经过:
ch19 environment baseline
ch20 HA failure model and drills
ch21 restore evidence
ch22 endpoint and connection behavior
ch23 security controls
ch24 approved SLI/SLO and ownership
ch25 signal and alert exercise
ch26 representative capacity一项数字若没有:
measurement
window
population
exclusions
owner
alert
runbook
evidence retention它只是愿望。
统一目标不等于统一部署
服务目标统一,是为了让组件协作;并不要求把组件放在同一主机、同一进程或 同一团队。
反过来,部署在同一 PostgreSQL cluster 也不自动意味着目标相同:
- 交易写入需要 read-your-writes;
- 离线分析允许 60 秒 lag;
- 备份任务关注可恢复性;
- 搜索 pilot 关注质量与构建时间。
平台要把这些 workload class 显式分开,并为冲突设优先级。
用一个“合同—证据—动作”闭环
每项服务能力最终应形成:
contract
owner + semantics + objectives + limits
evidence
SQL/catalog/metric/log/drill/manifest
decision
accepted / conditional / pilot / rejected
action
deploy / isolate / tune / externalize / rollback
review trigger
version / scale / incident / objective / ownership change本章的四份 JSON 和一个 ADR 正是在演示这个闭环。它们的价值不在文件格式, 而在于架构结论可以被程序拒绝、被证据更新,也可以在条件变化时退出。