# 先识别变化类型

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

---

升级风险首先取决于“什么发生了变化”。同一个“版本升级”工单，可能只是在同一 major
里换修复版，也可能跨越 data directory、系统目录、SQL 行为和扩展 ABI。若不先分类，
团队很容易把 minor upgrade 做成一次不必要的数据迁移，或把 major upgrade 当成滚动
重启。

## 30.1.1 小版本、安全修复与大版本 {#item-30-1-1}

### 版本号先按 PostgreSQL 规则读

从 PostgreSQL 10 开始：

```text
17 -> major
17.10 -> major 17 的第 10 个 minor release
17 -> 18 -> major upgrade
17.9 -> 17.10 -> minor upgrade
```

9.6 及更早版本使用前两段表示 major，例如 9.5→9.6 是 major upgrade，
9.6.23→9.6.24 才是 minor upgrade。不要用通用 SemVer 的
“major.minor.patch”直觉解释 PostgreSQL。

社区的[版本策略](https://www.postgresql.org/support/versioning/)给出两个硬边界：

- 每个 major 通常支持五年，超过 EOL 不再获得正常修复；
- 同一 major 的 minor release 不改变内部存储格式，官方建议运行当前 minor；
- major 会改变不向后兼容的 data directory，需要 dump/restore、`pg_upgrade` 或逻辑
  迁移；
- 可以跨多个 major 升级，但必须阅读所有跨越版本的 release notes。

分类表：

| 类型 | 数据目录 | 典型动作 | 仍需验证 |
|---|---|---|---|
| minor 修复 | 同 major 兼容 | 换二进制并重启 | release note、扩展包、HA 逐节点顺序 |
| 紧急安全修复 | 通常仍是 minor | 缩短审批与暴露时间 | CVE 触发面、临时缓解、升级后攻击面 |
| major | 不直接兼容 | 三类迁移路径之一 | 全部数据、行为、扩展、客户端和回退合同 |
| OS/libc/ICU 更新 | PG 版本可不变 | 系统维护 + 对象评估 | collation、TLS、动态库、驱动 |
| 扩展更新 | PG 版本可不变 | 包 + `ALTER EXTENSION` | update path、对象重建、不可降级 |

“安全修复”不是第四种存储格式；它是变更优先级。风险高的漏洞可能要求更快上线，但不能
免除备份、逐节点、兼容和回退检查。反过来，社区明确认为长期停留在旧 minor 往往比及时
升级风险更大。

### Pigsty 中的 minor rolling 仍有顺序

同一 major 的 HA 集群通常可以：

```text
准备并锁定同一套 minor/extension 包
  -> 升级 replicas
      -> 分别重启并等待重新追平
          -> switchover
              -> 升级原 primary
                  -> 重启、追平、验收
```

Pigsty 提供包仓、Ansible、Patroni 与 `pg` 管理命令来执行这条链，但“rolling”不等于
所有节点同时更新。每一步都应确认：

```sql
SELECT version(), pg_is_in_recovery();
```

并观察 replica replay、slot、客户端错误与业务 SLO。扩展 `.so` 必须与每个节点正在
运行的 server binary 匹配；只升级 primary 的扩展包，下一次 failover 可能把故障推给
replica。

major 不能靠“replica 先装 18、primary 仍跑 17”完成物理滚动升级。物理流复制要求同一
major；跨 major 需要逻辑复制或重建后的拓扑。

## 30.1.2 SQL 行为、系统目录、参数和默认值 {#item-30-1-2}

### release notes 要转成可执行差异

不要只读“新特性亮点”。major release notes 的 migration/incompatibilities 部分才是
升级清单的起点。以 17→18 为例，变化包括但不限于：

- `initdb` 默认启用 data checksums；
- `pg_upgrade` 开始保留大部分优化器统计，但 extended/custom/cumulative statistics
  仍不完整；
- generated column 默认形态、时区缩写解析、`VACUUM/ANALYZE` 对继承子表的行为变化；
- 旧 `psql` 对 PostgreSQL 18 的某些 CSV `\copy` 边界可能不兼容；
- 某些系统视图列或含义发生变化；
- 默认 collation provider 相关行为会影响全文检索与 `pg_trgm` 索引。

本章实验正是利用第一条真实边界：源 PG17 启用了 checksums，而一个用
`--no-data-checksums` 初始化的 PG18 目标被 `pg_upgrade --check` 拒绝：

```text
old cluster uses data checksums but the new one does not
```

这说明“新版本的默认值更安全”不能替代显式匹配。目标必须按源集群和新架构的合同
初始化。

### 四张差异表

升级 ADR 至少维护：

| 表 | 典型内容 | 证据 |
|---|---|---|
| removed/changed behavior | SQL、类型、权限、触发器、排序、错误码 | release notes + query corpus |
| parameter diff | 删除、重命名、默认值、单位、context | `pg_settings`、新旧配置渲染 |
| catalog diff | 列、view、enum/code、权限 | exporter/脚本 SQL 的兼容测试 |
| reserved words/API | parser、函数签名、客户端协议 | schema restore + driver test |

在旧环境导出当前设置时，不要只保存一个手工编辑过的 `postgresql.conf`：

```sql
SELECT name,
       setting,
       unit,
       source,
       sourcefile,
       sourceline,
       pending_restart
FROM pg_settings
ORDER BY name;

SELECT sourcefile,
       sourceline,
       seqno,
       name,
       setting,
       applied,
       error
FROM pg_file_settings
ORDER BY seqno;
```

第一张是最终有效值，第二张是配置文件解析结果。两者配合才能发现：

```text
同名参数被后续 include 覆盖
新版本已不认识旧参数
配置行语法错误
值已写入但需要 restart
环境变量 / ALTER SYSTEM / 命令行改变来源
```

同样地，HBA 要用 `pg_hba_file_rules` 解析，不能只做文本 diff。

### 系统目录不是跨 major 的稳定应用 API

监控、迁移脚本和内部工具经常直接读取 `pg_stat_*`、`pg_catalog`。这些是 PostgreSQL
公开能力，但列集合和含义可以随 major 演进。避免：

```text
SELECT * 后按列位置解码
假设枚举状态永远只有旧值
把 OID 当跨集群稳定标识
在 SQL 中硬编码 server 版本分支却不测试
```

应显式列名，并以 `server_version_num` 选择经过测试的查询：

```sql
SELECT current_setting('server_version_num')::int;
```

升级前在新版本空集群上先跑完整 exporter、备份、健康检查和自动化 SQL。目标不是
“SQL 不报错”而是输出字段、单位和告警语义仍正确。

## 30.1.3 驱动、连接池、备份与观察组件兼容 {#item-30-1-3}

### 兼容矩阵的单位是“实际组合”

不要写“JDBC 支持 PostgreSQL”。应记录：

```yaml
application: checkout-v4
runtime: Java 21
driver: pgjdbc x.y.z
pool: HikariCP x.y.z
proxy: PgBouncer x.y
old_server: PostgreSQL 17
new_server: PostgreSQL 18
auth: scram-sha-256 + TLS verify-full
session_features:
  - prepared statements
  - binary transfer
  - application_name
  - statement_timeout
tested_queries: checkout-corpus-v8
```

驱动测试至少覆盖：

- 认证、TLS、channel binding、证书和密码轮换；
- 参数类型推断、binary/text 编解码、timestamp、numeric、array、JSON、large object；
- server-side prepare、statement cache、batch、COPY；
- error SQLSTATE、generated keys、cancel、timeout；
- failover、连接重建、transaction status 和 read-only 标志。

PgBouncer/连接池还要覆盖 session state。升级切换时保留的旧连接不会自动变成新版本
连接；prepared statement、临时表、`SET`、advisory lock 和 transaction pooling 的语义
需要按实际模式测试。发布证据必须包含“新建连接看到目标 system identifier”，而不只
看 pool 端口可达。

### 备份工具要同时验证生产与恢复端

逻辑迁移通常应使用**目标版本**的 `pg_dump` 读取旧 server。官方说明：新版
`pg_dump` 可以读取受支持的旧 server，且输出面向相同或更高 server；旧版 `pg_dump`
会拒绝读取更高 major，dump 输出也不保证能恢复到更低 major。

这带来几条门禁：

```text
new pg_dump -> old source   must pass
new pg_restore -> new target must pass
backup agent -> old during window must pass
backup agent -> new after cutover must pass
restore tooling -> isolated new cluster must pass
```

物理备份则与 server major、WAL、control file 和工具版本绑定。不能把 PG17 的物理备份
直接恢复成 PG18；它应先恢复为 PG17，再走获批的升级路径。Pigsty 中 pgBackRest 配置、
repository、stanza、retention 和恢复脚本都要在新拓扑重验，而不是只确认最新 backup
状态是 completed。

### “监控没报警”可能是 exporter 已失明

升级后指标归零有两种解释：

```text
系统没有负载
采集 SQL 已失败或字段含义改变
```

因此观察组件要做三向核对：

| 组件 | 新版本检查 | 原生对照 |
|---|---|---|
| exporter | scrape success、query errors、字段数 | `pg_stat_*` 原始 SQL |
| Grafana/Pigsty 面板 | 时间序列连续、label 未漂移 | server identity 与真实 workload |
| log pipeline | 新错误格式、severity、采集延迟 | PostgreSQL 本地 log |
| alert rule | 正例能触发、恢复能关闭 | 人工受控 canary |

升级测试应主动产生一条查询、一个可识别错误和一小段 WAL，确认应用、日志、指标和告警
四条链都看到同一时间线。只有全部组件对新 major 给出有意义的数据，才叫“观察能力
兼容”。

---

[返回本章目录](../) · [下一节：三类大版本升级路径](../02/) ·
[查看全书目录](/toc/) · [查看索引中心](/indexes/)
