# 比较分布式候选

LLMS 索引： [llms.txt](/llms.txt)

---

候选比较的第一步，是保留“不分布”作为对照组。

如果表格只有三种分布式产品，团队会被迫在它们之间选一个；如果加入：

```text
optimized primary
offline replica
independent analytical PostgreSQL
materialized summary
```

很多问题会暴露为负载隔离或数据粒度问题，而不是横向分片问题。

本节不替读者宣布一个普遍赢家，而是建立同口径的比较方法。

## 17.4.1 PostgreSQL 扩展、兼容数据库与专用分析系统 {#item-17-4-1}

### 候选地图

可以按“离 PostgreSQL 原生语义有多远”分层：

```text
Layer 0: same PostgreSQL instance
  indexes / partitions / parallel query / summaries

Layer 1: same physical PostgreSQL data
  read replica / offline replica

Layer 2: another PostgreSQL data copy
  logical replication / CDC / ETL to analytical PostgreSQL

Layer 3: PostgreSQL extension or federation
  postgres_fdw / Citus / workload-specific extensions

Layer 4: PostgreSQL-wire or SQL-compatible distributed database
  separate storage, transaction and operations implementation

Layer 5: specialized analytical system
  columnar/vectorized engine, separate ingestion and lifecycle
```

层数越高不表示越先进，只表示需要重新验证的语义越多。

### 对照组：优化后的 PostgreSQL

它应包含：

- 正确 schema 与统计信息；
- 代表性索引和 partition pruning；
- 并行计划与并发预算；
- 合理物化/汇总；
- workload guardrails；
- 当前硬件与一个可行纵向规格；
- offline replica 或独立分析副本候选。

若分布式候选只比未经调优的原表扫描快，比较没有意义。

### `postgres_fdw`：联邦访问与机制实验

`postgres_fdw` 把远端 PostgreSQL 表映射为 foreign table：

```text
CREATE EXTENSION
  -> CREATE SERVER
  -> CREATE USER MAPPING
  -> CREATE FOREIGN TABLE / IMPORT FOREIGN SCHEMA
  -> SELECT / DML
```

它能做：

- 远端过滤和列裁剪；
- 某些 JOIN/聚合下推；
- 外表分区；
- 联邦读取；
- 迁移期间的过渡；
- 把远端数据物化到本地；
- 显示远端 SQL 和数据流。

它不应被默认理解为一个完整的透明分布式数据库。路由、rebalance、全局
约束、协调端 HA、全局备份点和很多运维职责仍需自行设计。PostgreSQL 18 的
`postgres_fdw` 远端事务也不支持 prepare 为两阶段提交。

本章用它是因为机制透明：

```text
Foreign Scan
Remote SQL
actual rows
user mapping
server failure
```

都能直接观察。它是很好的教学镜子，不是本章对生产选型的默认推荐。

### Citus：PostgreSQL 扩展式分片候选

Citus 把表区分为 distributed、reference、local 等类型，以分布列决定行的
放置；相关表按相同分布键 colocate 后，单租户查询和某些 JOIN 可以在一组
共置分片上执行。跨租户聚合则可由 worker 产生 partial result，再由协调端
合并。

这使它适合评估：

```text
multi-tenant workload with tenant-local transactions
real-time aggregate workload with decomposable computation
PostgreSQL ecosystem continuity
```

但分布键选择成为 schema 与查询合同。Citus 官方
[Choosing Distribution Column](https://docs.citusdata.com/en/stable/sharding/data_modeling.html)
强调 tenant/entity key、co-location 与跨节点数据移动之间的关系；小型共享
维表可评估 reference table。

需要验证：

- row-based 还是 schema-based sharding；
- distribution column 是否出现在 PK/FK/查询；
- colocated tables 与 reference tables；
- 单租户与全局查询比例；
- rebalance 与大租户隔离；
- coordinator/worker HA；
- distributed DDL；
- backup/restore；
- 版本升级和扩展组合；
- Pigsty L1 的实际拓扑。

本章 loopback FDW 的“同分片 JOIN 未下推”不能直接外推为 Citus 行为；它只
提醒读者对目标产品的目标 SQL 看实际计划。

### PostgreSQL-compatible distributed database

这类系统可能支持 PostgreSQL wire protocol、部分 SQL、驱动和工具，让迁移
起步更容易。必须拆开“兼容”：

```text
wire protocol
parser syntax
catalog shape
data types
functions/operators
transaction semantics
isolation/locking
extensions
backup/restore
monitoring
operational commands
```

一个应用只用简单 `SELECT/INSERT`，兼容度可能足够；一个应用依赖 PostGIS、
自定义 operator class、logical decoding、advisory lock、trigger、COPY、
RLS 和精细 catalog 查询，迁移面完全不同。

不要用厂商兼容百分比替代自己的 feature inventory。

### 专用分析系统

专用 OLAP 系统通常优化：

```text
columnar compression
large scans
vectorized execution
distributed aggregation
high analytical concurrency
object storage / tiering
```

它可能显著优于 PostgreSQL 处理某类宽表聚合，但会引入第二套：

```text
ingestion / CDC
schema mapping
data freshness
deduplication
late-event handling
access control
backup/recovery
monitoring/on-call
cost model
query semantics
```

如果 OLTP truth 仍在 PostgreSQL，必须定义两个系统不一致时谁是事实来源。

### 候选能力矩阵

以下不是产品评分，而是评审问题：

| 维度 | 单机/副本 | FDW | Citus 类扩展 | 兼容分布式库 | 专用 OLAP |
|---|---|---|---|---|---|
| 原生 PG 语义 | 最高 | 本地/远端 PG | 高但分片有边界 | 必须实测 | 通常较低 |
| 单租户局部性 | 原生 | 手工路由 | 分布键核心 | 依实现 | 依模型 |
| 全局聚合 | 单节点 | 可下推/合并 | 分布式 partial/final | 依实现 | 核心场景 |
| 跨分片事务 | 不适用 | 有明显限制 | 需按版本/形状验证 | 需验证 | 通常非 OLTP 重点 |
| 扩展生态 | 原生 | 两端一致性 | 需验证组合 | 通常有限 | 不适用/自有 |
| 运维体系 | 已有 | 多 PG + 编排 | coordinator/workers | 新体系 | 第二套体系 |
| 新鲜度 | 即时/复制 lag | 远端即时视连接 | 即时视事务 | 依实现 | CDC/批次 lag |
| 退出成本 | 低 | 中 | 中高 | 高 | 高 |

矩阵中的每个“需验证”都要变成 PoC 用例。

### 所有权边界

候选不仅有技术 owner：

| 责任 | 必须有人承担 |
|---|---|
| schema 与分布键 | 数据模型 owner |
| query migration | 应用 owner |
| ingestion/CDC | 数据平台 owner |
| cluster lifecycle | DBA/SRE |
| correctness reconciliation | 业务 owner + 数据 owner |
| incident decision | on-call |
| cost | 预算 owner |
| exit | 项目 sponsor |

缺少 owner 的候选不能因为跑分快进入生产。

## 17.4.2 SQL 兼容不等于事务、扩展和运维兼容 {#item-17-4-2}

### 建立兼容性分层

推荐至少分八层：

```text
L1 protocol
L2 syntax
L3 type and expression semantics
L4 transaction and concurrency
L5 schema objects and extensions
L6 planner/performance behavior
L7 operations and observability
L8 failure/recovery and lifecycle
```

只有 L1/L2 通过，应用仍可能在 L3–L8 失败。

### 协议兼容

验证：

- TLS、SCRAM、GSS/SSO；
- connection parameters；
- prepared statements；
- binary/text format；
- COPY；
- cancellation；
- notices/errors 与 SQLSTATE；
- connection pool transaction/session mode；
- driver features；
- failover reconnect。

能用 `psql` 登录只是起点。

### SQL 与类型语义

测试应用真实使用的：

```text
numeric precision and rounding
timestamp/time zone
collation and locale
NULL ordering
JSON/JSONB
arrays/ranges/multiranges
generated columns
identity/sequence
UPSERT/MERGE/RETURNING
CTE/window/lateral
recursive query
```

同样语法若类型、collation 或时区不同，会返回不同结果。

`postgres_fdw` 官方文档也建议 foreign table 的类型和 collation 与远端精确
匹配，否则本地与远端对条件的解释可能不同。它还不会自动导入除 `NOT NULL`
之外的约束，因为错误约束可能导致规划器做出不安全推断。

### 事务与并发语义

需要独立验证：

```text
READ COMMITTED snapshot
REPEATABLE READ
SERIALIZABLE
row/table/advisory locks
deadlock detection
savepoints
DDL transactionality
cross-shard transaction
retry error classes
sequence behavior
```

例如“支持 serializable”不够，要验证：

- 冲突时 SQLSTATE；
- 是否需要 client retry；
- 多分片是否同样保证；
- range/predicate conflict 如何实现；
- failover 后 in-flight transaction 的结果；
- unknown commit 如何对账。

### schema 与扩展兼容

列清单：

```sql
SELECT
  extname,
  extversion
FROM pg_catalog.pg_extension
ORDER BY extname;
```

对每个扩展检查：

- 是否可安装；
- exact version；
- trusted/non-trusted；
- shared preload；
- 类型、函数、operator、index AM；
- logical/physical replication；
- backup/restore；
- rolling upgrade；
- 每个节点一致性。

不能把“兼容 PostgreSQL”理解为兼容任意 PostgreSQL 扩展。

### catalog 兼容

许多工具读取：

```text
pg_catalog
information_schema
pg_stat_*
pg_locks
pg_settings
pg_extension
pg_class/pg_attribute/pg_index
```

候选可能接受这些查询但字段为空、语义不同或只反映 coordinator。验证：

- migration tool；
- ORM introspection；
- monitoring exporter；
- backup tool；
- schema diff；
- incident runbook；
- 自定义运维脚本。

### planner 兼容

相同 SQL 的计划可以完全不同。需要观察：

```text
where execution happens
which shards are pruned
which filters/joins/aggregates push down
how many rows cross network
how coordinator merges
what spills
what happens under skew
```

本章同一个 FDW 月报有两种 SQL：

```text
parent aggregate:
  fetch 240,000 facts
  aggregate locally

explicit per-shard daily aggregate:
  fetch 480 + 480 aggregate rows
  aggregate monthly locally
```

语义相同，执行位置不同。兼容性测试不能只检查最终 rows。

### 运维兼容

对比日常动作：

| 动作 | 问题 |
|---|---|
| provision | 声明、包、密钥、节点身份如何？ |
| scale | 加节点是否自动，旧数据如何移动？ |
| backup | 一致点、加密、保留、校验如何？ |
| restore | 空环境恢复、route metadata 如何？ |
| failover | coordinator/worker 谁仲裁？ |
| upgrade | rolling、停机、扩展顺序？ |
| schema change | fan-out、失败补偿？ |
| observability | 全局与每 shard 指标？ |
| security | HBA、证书、user mapping、secret？ |
| decommission | 数据擦除与证明？ |

工具名字相同也不表示语义相同。例如在 coordinator 执行 `VACUUM` 是否覆盖
所有 shard，要由目标产品和版本证明。

### 错误兼容

应用通常围绕 SQLSTATE 决定：

```text
retry
abort
return conflict
mark dependency unavailable
```

候选必须保留或重新映射这些错误语义。测试：

- unique violation；
- serialization failure；
- deadlock；
- lock timeout；
- statement timeout；
- connection failure；
- read-only transaction；
- insufficient privilege；
- disk/full or quota；
- shard unavailable。

只测试成功路径会让第一场故障变成兼容性测试。

### 精度与顺序

分析结果的隐性差异：

```text
floating aggregate order
approximate distinct
collation sort
NULL order
time zone database version
decimal scale
non-deterministic top-N ties
```

冻结输出应：

- 使用 exact numeric 或定义误差；
- 明确 `ORDER BY` 与 tie-breaker；
- 固定时区/locale；
- 记录 approximate 算法/version/seed；
- 对 checksum 使用稳定序列化。

## 17.4.3 用同一工作负载和失败条件比较 {#item-17-4-3}

### 先冻结语义

比较协议应先固定：

```text
schema
data generator / snapshot
business queries
expected results
freshness point
concurrency schedule
failure schedule
versions/config
measurement method
```

本章：

```text
fixture = ch17-analytics-v1
frozen_at = 2026-07-29T00:00:00Z
monthly rows = 32
monthly checksum = 644d45544ebbc2a80c42270c38ac6885
```

任何候选先生成同一月报，再谈性能。

### workload suite

至少包含：

1. **高选择性单租户读**

   ```sql
   WHERE tenant_id = 3
     AND occurred_on >= DATE '2026-04-01'
   ```

   验证 pruning、index、route、read amplification。

2. **全局可分解聚合**

   count/sum 按租户和月份，验证 partial/final 与传输。

3. **同分片 JOIN**

   account + sales，验证真实 join pushdown。

4. **非分布键 JOIN**

   故意触发 shuffle 或拒绝，量化代价。

5. **小写入与幂等重试**

   验证事务、unique、retry。

6. **批量装载**

   验证 ingest、WAL/replication、rebalance。

7. **schema change**

   增列、建索引、backfill，验证 mixed-version。

8. **backup/restore**

   从空环境恢复并跑 checksum。

### 数据规模阶梯

不要只跑一个尺寸：

```text
S: fits in memory
M: working set near memory
L: exceeds memory
XL: near storage/maintenance target
```

观察曲线和拐点，而不是挑一张最好看的柱状图。

### 冷暖缓存

至少区分：

```text
warm repeated query
cold/evicted data
after restart
after rebalance
after restore
after schema/index build
```

专用分析系统和 PostgreSQL 对缓存、编译、数据格式转换的预热不同。只比较
第十次执行或只比较第一次执行都可能偏颇。

### 并发与到达模型

开放环和封闭环会得出不同结论：

```text
closed-loop:
  client waits for response, then sends next
  overloaded system self-throttles

open-loop:
  requests arrive by schedule independent of completion
  queueing and overload become visible
```

生产若有固定到达率，基准不能只用少量客户端闭环。还要记录 client queue 与
server queue，避免 coordinated omission。

### 资源和成本同报

每个结果同时报告：

```text
latency distribution
throughput
error rate
CPU seconds
memory peak
storage read/write
temp/spill
network bytes
WAL/replication
storage footprint
node count
operator time
license/cloud cost
```

“P95 快 2 倍但使用 8 倍节点”与“同成本快 2 倍”不是同一结论。

### 失败矩阵

对每个候选执行：

| 故障 | 验证 |
|---|---|
| query cancel | 远端工作是否停止、资源是否释放 |
| worker/shard down | 单分片与全局查询如何返回 |
| coordinator down | 新连接、已有事务、恢复 |
| network partition | timeout、unknown commit、重试 |
| disk pressure | backpressure 与告警 |
| replica lag | freshness 标识与路由 |
| rebalance interrupted | resume、重复/遗漏 |
| schema node drift | 拒绝、修复与可见性 |
| backup during load | 恢复一致性 |

失败结果必须是验收的一部分，不是“以后做 chaos”。

### 本章的最小失败条件

冻结 PoC 至少证明：

```text
application write -> SQLSTATE 42501
shard B unreachable -> global query SQLSTATE 08001
shard B unreachable -> tenant 2 scoped read = 30000
server catalog after rollback = before failure
```

它没有证明：

- 真实网络分区；
- process kill；
- WAL/replica behavior；
- coordinator HA；
- shard failover；
- in-flight distributed write；
- rebalance resume。

这些是生产 PoC 的追加用例。

### benchmark result template

每条结论写成：

```text
claim:
  two-stage aggregation reduces coordinator input

environment:
  PostgreSQL 18.6, postgres_fdw 1.2
  same-host/same-instance loopback

input:
  ch17-analytics-v1, 240,000 facts

evidence:
  naive Append actual rows=240000
  two-stage Append actual rows=960
  both byte-identical to frozen monthly CSV

scope:
  row-transfer shape only

not proven:
  network bytes, latency, throughput, HA, scaling
```

这个模板迫使作者把结论和外推边界放在一起。

### 评分前设置 veto

某些条件不应靠加权平均掩盖：

```text
incorrect result
cannot meet RPO/RTO
unsupported mandatory extension
unacceptable data residency
no recoverable backup
license conflict
no exit path
unknown-commit without business reconciliation
```

任何 veto 失败，候选退出；不能用“查询快 30%”抵消。

### 决策表

通过 veto 后再评分：

| 维度 | 权重 | 证据 | 分数 | 不确定性 |
|---|---:|---|---:|---|
| correctness | veto | golden/checksum | pass | low |
| workload SLO | 25 | representative replay |  |  |
| failure/RPO/RTO | 20 | drills |  |  |
| operability | 15 | day-2 tasks |  |  |
| compatibility | 15 | feature inventory |  |  |
| cost | 10 | same horizon/TCO |  |  |
| migration | 10 | rehearsal |  |  |
| exit | 5 | reverse rehearsal |  |  |

“不确定性”单列，避免没有验证的候选因为乐观估分胜出。

### 本节结论

比较方法的输出不是产品排行榜，而是：

```text
baseline
candidate contract
evidence bundle
known limitations
veto results
cost/ownership
migration and exit
decision trigger
```

下一节构造一个最小 FDW PoC，目的不是给候选打性能分，而是验证“租户路由、
远端聚合、权限和部分失败能否被证据化”这一个关键假设集合。

---

[上一节：何时需要分布式](../03/) · [返回本章目录](../) · [下一节：部署最小分布式 PoC](../05/) ·
[查看全书目录](/toc/) · [查看索引中心](/indexes/)
