# 三类大版本升级路径

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

---

大版本升级没有统一最优解。路径选择本质上是在四种成本之间交换：

```text
业务不可写时间
额外计算/存储资源
变更系统数量与复杂度
回退与数据对账难度
```

先写约束，再选工具；不能因为团队最熟 `pg_upgrade`，就把所有数据库都压进一个停机
窗口。

## 30.2.1 `pg_upgrade` 与停机窗口 {#item-30-2-1}

### 它升级 cluster，不是逐条重写业务数据

`pg_upgrade` 用新版本创建系统目录，再迁移旧 cluster 的 catalog 与用户 relation 文件，
因此通常比逻辑 dump/restore 快得多。它要求：

- old/new server binaries 与 data/config directories 都可用；
- compile-time 与 control-data 条件兼容；
- 新 major 对应的扩展 shared libraries 已安装；
- 两个 cluster 在正式 upgrade 时都停止；
- 操作系统用户/数据库 install user、认证和 socket 可连接；
- tablespace、standby、slot、统计与生成的 rebuild scripts 都有计划。

永远运行**新版本**的 `pg_upgrade`：

```bash
/usr/lib/postgresql/18/bin/pg_upgrade \
  --old-bindir /usr/lib/postgresql/17/bin \
  --new-bindir /usr/lib/postgresql/18/bin \
  --old-datadir /pg/data/17 \
  --new-datadir /pg/data/18 \
  --username postgres \
  --socketdir /secure/short/socket \
  --check
```

`--check` 应在彩排和正式窗口前重复执行。若计划使用特定传输模式，检查时也要带相同
模式，才能覆盖文件系统约束。

### 文件传输模式决定速度和回退形态

| 模式 | 空间/速度 | old cluster 可回退边界 |
|---|---|---|
| default `--copy` | 需要完整副本，较慢 | old relation files 独立，新端启动后仍可启动旧端 |
| `--copy-file-range` | 可能利用高效内核复制 | 取决于实际文件系统行为，仍需实测 |
| `--clone` | CoW 文件系统上快且省空间 | 新旧逻辑独立，但要求文件系统支持 |
| `--link` | 硬链接，最快且省空间 | 新端一旦启动写共享文件，旧端不再安全 |
| `--swap` | 可能更快，直接交换目录内容 | 传输进入破坏阶段后旧端不再安全 |

不能把 `--link` 的分钟级结果与 `--copy` 的回退承诺同时写进 runbook。选择哪个模式，
就必须演练哪个模式、同一种文件系统、同样 tablespace 布局与数据规模。

本章为了验证“目标零写入时旧端可启动”，明确使用 `--copy`，禁止 link、clone、swap
和 `--no-sync`。升级完成后新 PG18 已启动并校验，随后停止新端、启动旧 PG17，
10,000 行 manifest 仍相等。这个结论只属于 copy 模式和本次 fixture。

### 停机窗口不只是一条命令的秒数

业务不可写窗口通常包含：

```text
drain writers
  -> final checkpoint / backup evidence
      -> stop old topology
          -> pg_upgrade checks and transfer
              -> rebuild scripts / extension updates / collation work
                  -> staged ANALYZE
                      -> application validation
                          -> routing and pool drain
```

`pg_upgrade` 输出的 `Upgrade Complete` 只是中点。它会提示需运行的 rebuild/reindex
脚本；被这些脚本引用的表在完成前可能返回错误结果或性能极差。PostgreSQL 18 会迁移
大部分优化器统计，但 extended、扩展自定义和累计统计并不完整，仍建议：

```bash
vacuumdb --all --analyze-in-stages --missing-stats-only
vacuumdb --all --analyze-only
```

彩排要分别计时每一阶段，以生产数据克隆的 P95/P99 结果安排窗口，不能拿一个 10,000
行实验的 `pg_upgrade` 时间乘比例。

## 30.2.2 逻辑复制与渐进切换 {#item-30-2-2}

逻辑复制把新 major 建成独立 cluster：

```text
old source remains writable
  -> schema prepared on new target
      -> initial copy
          -> incremental apply
              -> shadow read and reconciliation
                  -> freeze, final marker, sequence sync
                      -> cutover
```

它的优点：

- 业务不可写时间主要集中在最终冻结与切流；
- 新旧环境可同时运行，便于真实应用影子验证；
- 可以跨平台、改变物理布局、重配参数和扩展；
- 目标可逐步扩容与预热。

代价已经在第 29 章验证：

- DDL、sequence、large object 与许多对象不自动复制；
- publication/subscription、replica identity、slot WAL 必须治理；
- 目标写入会制造冲突或 silent drift；
- 全量、增量、业务不变量和切流路由都需独立证据；
- 回退在目标承接写入后需要 reverse sync 或 reconciliation。

跨 major 还要检查 row/column filter、generated columns、binary transfer 与协议选项在
**两个版本交集**中的语义。一般先升级 subscriber/target 能扩大兼容余量，但具体拓扑
需按官方 logical replication upgrade 章节执行。

PostgreSQL 17 起，`pg_upgrade` 可以在满足前提时迁移 logical slot/subscription
依赖；这不意味着任意逻辑复制拓扑能自动升级。publisher upgrade 前需要停用对应
subscription，循环、多节点和 two-phase 拓扑有非事务步骤，必须有备份和逐节点状态机。

Pigsty 推荐生产 major upgrade 优先考虑新建集群 + 逻辑迁移，因为它把应用验证和资源
准备移到切换前。这里的“推荐”仍需服从数据对象是否可逻辑复制、写入率、slot 容量、
DDL 频率和团队能否可靠完成对账。

## 30.2.3 dump/restore 与重建机会 {#item-30-2-3}

dump/restore 是最“逻辑化”的升级：在新 cluster 按新版本规则重新创建对象和写入行。
它成本高，却也是一次摆脱历史物理包袱的机会：

- 重排 tablespace、partition 与对象 owner；
- 只迁移仍需保留的数据；
- 统一 encoding/locale 的新建策略；
- 重建全部 index，消除旧物理布局；
- 审阅 schema、extension、privilege 和废弃对象；
- 用 directory archive 并行 dump/restore。

常见路径：

```bash
# 用目标版本工具读取旧 server
pg_dump -Fd -j 8 -d app -f app.dump
pg_dumpall --globals-only > globals.sql

# 在新 server 恢复并审阅错误
psql -X -d postgres -f globals.sql
pg_restore -j 8 -d app_new app.dump
```

选择性恢复不是“完整 cluster 迁移”的同义词。需要另外处理：

```text
roles and memberships
database-level settings
tablespaces
extensions and shared libraries
large objects
publications/subscriptions
security labels
replication slots
external files and FDW credentials
```

使用 target major 的 `pg_dump`，并读取 stderr 全部 warning。directory 格式是唯一支持
parallel dump 的 archive；custom/directory 都支持选择与重排 restore。并行增加
`jobs + 1` 个连接和源端负载，还可能因 DDL lock 排队而失败，必须在生产形态彩排。

dump/restore 的回退与逻辑迁移相似：旧端可继续保留，但目标开始承接独占写入后，改回
连接串仍会丢新事实。它不是因为“重建了一份”就天然可逆。

## 30.2.4 没有一种路径天然“滚动无感” {#item-30-2-4}

### 用约束选型

| 约束 | `pg_upgrade` | logical replication | dump/restore |
|---|---:|---:|---:|
| 最小写停机 | 较弱 | 强 | 较弱 |
| 额外硬件 | 可同机 | 通常需要完整新集群 | 通常需要完整新集群 |
| 升级速度 | 通常最快 | 取决于全量 + 追平 | 通常最慢 |
| 真实影子验证 | 有限 | 最强 | 恢复完成后可做 |
| 改物理布局 | 有限 | 强 | 最强 |
| DDL/sequence 编排 | 中 | 最复杂 | restore 负责大部分 |
| 旧端物理回退 | 取决于 copy/link/swap | 切流前强 | 切流前强 |
| 数据对账 | 必需 | 最重 | 必需 |

一个常见组合不是三选一，而是：

```text
production: logical replication
  + disaster rehearsal: dump/restore
  + small internal clusters: pg_upgrade
```

甚至同一项目会先用 dump/restore 生成测试克隆，再用 `pg_upgrade` 彩排正式路径。

### “无感”要拆成多个 SLO

所谓无感至少包含：

```text
connection establishment
read availability
write availability
transaction in flight
latency and plan stability
background jobs
CDC and replica continuity
error and retry semantics
data freshness
```

逻辑切换只有几秒写冻结，不代表旧池里的长事务、prepared statement、sequence、
缓存、ETL 与外部副作用无感。minor rolling 也会发生连接断开和 failover。`pg_upgrade`
即使文件迁移很快，post-upgrade reindex/ANALYZE 仍可能控制上线时间。

所以 runbook 不写“无感升级”，而写：

```yaml
read_unavailable_budget: 5s
write_unavailable_budget: 30s
inflight_policy: drain-then-retry-idempotently
replication_rpo: 0
latency_gate: p99 <= baseline * 1.20
rollback_boundary: before first target-only commit
```

可测量的预算才是发布决策；“滚动”“在线”“秒级”只是实现特征。

---

[上一节：先识别变化类型](../01/) · [返回本章目录](../) · [下一节：扩展与依赖升级](../03/) ·
[查看全书目录](/toc/) · [查看索引中心](/indexes/)
