# 版本与数据库初始化契约

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

---

安装包可以升级，初始化事实却不一定能在线改。版本与初始化契约要在第一次
创建数据目录前冻结，并在创建后从 PostgreSQL 自己读取回来。

本节正式契约是：

```text
Pigsty release       v4.5.0
PostgreSQL major     18
captured patch       18.6 / server_version_num 180006
encoding             UTF8
locale/provider      C.UTF-8 / builtin
data checksums       on
block size           8192 bytes
WAL segment size     16777216 bytes
timezone             Etc/UTC
password verifier    scram-sha-256
TLS                  on
```

这里的 `captured patch` 是验收时观察值，不是允许集群成员长期运行不同小版本。

## 19.4.1 PostgreSQL、locale、collation、编码与 checksum {#item-19-4-1}

### “PostgreSQL 18”不是完整版本身份

至少要区分：

```text
major version       18
patch version       18.6
package release     distribution/vendor build revision
server build        configure options, compiler, architecture
client version      psql/libpq/driver
extension version   vector/PostGIS/... each separately
automation release  Pigsty 4.5.0
```

major 决定磁盘格式、系统目录、SQL/行为兼容边界；patch 主要承载 bug 与安全
修复。扩展和操作系统包又有各自版本。只写“PG18”无法重建环境。

从 server 读取：

```sql
SELECT version();
SELECT current_setting('server_version') AS server_version,
       current_setting('server_version_num')::integer AS server_version_num;
```

从 package manager、容器 image digest 或 artifact repository 记录 package
身份。不要用 `psql --version` 代替 server 版本；它只说明客户端。

### database cluster、database 与 locale 的层次

PostgreSQL 官方
[`initdb`](https://www.postgresql.org/docs/18/app-initdb.html)
创建一个 database cluster：数据目录、共享系统目录和
`postgres`、`template1`、`template0`。随后 `CREATE DATABASE` 通常从模板
复制。

因此初始化默认会向后传播：

```text
initdb default
    -> template databases
        -> new database defaults
            -> per-column/per-expression COLLATE override
```

不要把 OS 的 `LANG`、cluster 默认和某个 database 的 locale 混为一谈。

### encoding 解决字节到字符，不解决语言排序

`UTF8` 定义字符编码。它不自动定义：

- “ä”排在什么位置；
- 大小写转换规则；
- 字符类别；
- 模糊/前缀查询的索引语义；
- Unicode 版本变化后的排序稳定性。

这些主要属于 collation/provider。

查当前 database：

```sql
SELECT datname,
       pg_encoding_to_char(encoding) AS encoding,
       datlocprovider,
       datcollate,
       datctype,
       datlocale,
       datcollversion
FROM pg_database
WHERE datname = current_database();
```

PG18 的字段反映 provider 与 locale；跨版本工具不要假设每个字段都存在。

### collation 是数据语义，不是显示偏好

PostgreSQL 的
[Collation Support](https://www.postgresql.org/docs/18/collation.html)
说明 collation 会参与：

```text
ORDER BY / comparison
lower / upper / initcap
pattern matching
collatable expression
index ordering and uniqueness semantics
```

这会产生一个重要结论：

> 改变 collation provider 或版本，可能改变既有索引所依据的顺序；不能把
> 它当成客户端展示参数。

生产选择至少比较：

| provider | 来源 | 优势 | 需要管理的变化 |
|---|---|---|---|
| `builtin` | PostgreSQL 内建 | 跨 OS 可预测，部署依赖少 | 受 PostgreSQL major 语义约束 |
| `libc` | 操作系统 C library | 与 OS locale 集成 | OS/glibc locale 版本漂移 |
| `icu` | ICU library | 多语言与定制能力强 | ICU 版本、包与索引刷新 |

本章选 PG17+ 可用的内建 `C.UTF-8`。Pigsty 的
[资源准备建议](https://pigsty.io/docs/deploy/prepare/)
也推荐 PG17+ 以它作为默认。这个选择偏向稳定、可预测的数据库基础排序；
它不声称提供每种自然语言的用户期望顺序。需要语言相关排序时，应明确
column/expression collation，并有业务样例。

### `C.UTF-8` 与 `C` 不能只看名字

需要记录：

```text
locale string
locale provider
encoding
collation version
PostgreSQL major
```

相同 `C.UTF-8` 字符串由不同 provider 提供时，不应靠名称推断行为完全一致。
本章验收同时要求：

```text
encoding=UTF8
locale_provider=builtin
datlocale/datcollate/datctype contains C.UTF-8
```

### 初始化时显式选择，创建后从目录复核

PG18 `initdb` 的 provider 默认仍是 `libc`；选 `builtin` 必须显式指定合法
builtin locale。不要因为 Pigsty wizard 给出了合理结果，就误记成 PostgreSQL
自己的默认。

等价意图可以表达为：

```text
--encoding=UTF8
--locale-provider=builtin
--builtin-locale=C.UTF-8
```

在 Pigsty 中由 `pg_encoding`、`pg_locale` 及生成的 Patroni bootstrap
配置承载。最终证据仍是 `pg_database`，不是模板文件。

### checksum 检测静默页损坏，不创造副本

data checksum 在读页时帮助发现 I/O 系统造成的静默损坏。它不能：

- 修复坏页；
- 替代 backup；
- 替代 replica；
- 防止错误 SQL；
- 证明 storage 持久性；
- 覆盖 WAL 文件的所有损坏形态。

PG18 `initdb` 默认启用 checksum，也允许显式
`--no-data-checksums`。因此不能用“版本是 18”推断某个现存 cluster 已启用。

读取：

```sql
SHOW data_checksums;

SELECT datname, checksum_failures, checksum_last_failure
FROM pg_stat_database
ORDER BY datname;
```

`on` 表示检测机制启用，不表示从未发生 storage 问题；failure counter、
日志、scrub/backup 验证要一起看。

checksum 可以通过离线工具改变，但那仍是停机、容量和回退都要规划的
maintenance。对新平台，把它当初始化契约更简单。

### 本章的验证 SQL

[`postgresql-facts.sql`](/labs/ch19/postgresql-facts.sql)
一次读取：

```text
cluster_name
server_version_num
pg_is_in_recovery()
system identifier / timeline
checksum, block, WAL segment
timezone, password_encryption, SSL
database encoding/provider/locale
installed extensions
```

它以 JSON 输出，避免人眼从四台机器复制表格时错列。

## 19.4.2 WAL、页大小、扩展与认证前提 {#item-19-4-2}

### block size 属于二进制与磁盘格式合同

本章观察：

```sql
SHOW block_size;       -- 8192
```

8 KiB 是常见构建值。它影响 relation page、buffer、部分容量公式和工具
兼容。不要假设所有自编译发行版都相同，也不要把 OS filesystem block size
当成 PostgreSQL page size。

恢复、物理复制、低层工具与扩展需要与 server build 兼容。

### WAL segment size 只能在初始化时选择

PG18
[`initdb --wal-segsize`](https://www.postgresql.org/docs/18/app-initdb.html)
接受 1–1024 MiB 的 2 次幂，默认 16 MiB，而且只能初始化时设置。

本章冻结：

```sql
SHOW wal_segment_size; -- 16MB
```

segment 大小不改变 WAL 逻辑正确性，但会影响：

```text
archive object granularity
directory file count
shipping/retention operations
monitoring formula
tool assumptions
```

改变它不是调一个 reload 参数，而是重建/迁移问题。

### `wal_level` 等运行参数不是全部初始化事实

要区分三类：

| 类型 | 例子 | 典型改变方式 |
|---|---|---|
| compile/init | page size、WAL segment、system identifier、默认 locale | rebuild/init/migrate |
| restart | `shared_preload_libraries`、部分 WAL/worker 参数 | rolling maintenance |
| reload/session | 许多 planner/logging/timeout 参数 | controlled reload/role policy |

同样一个配置文件中的两行，变更成本可能完全不同。变更系统应记录
`context`：

```sql
SELECT name, setting, unit, context, pending_restart, source, sourcefile
FROM pg_settings
WHERE name IN (
  'wal_level',
  'max_wal_senders',
  'max_replication_slots',
  'shared_preload_libraries',
  'password_encryption',
  'ssl'
);
```

### system identifier 界定物理 cluster 身份

`pg_control_system()` 可读取 system identifier：

```sql
SELECT system_identifier,
       pg_control_version,
       catalog_version_no
FROM pg_control_system();
```

本章要求：

```text
pg-meta members   one system identifier
pg-test members   another system identifier
two clusters      identifiers differ
```

它能揭露把同一 data directory/复制链误报成两个服务单元的错误。它不是
secret，但属于运维身份；对外报告可只保存关系或受控 evidence。

timeline 不是 cluster identifier。promotion 后 timeline 可以改变，system
identifier 保持。第 20 章会使用这个区别。

### 扩展契约从 package 开始，不从 `CREATE EXTENSION` 开始

对每个扩展记录：

```text
supported PG majors
OS/architecture packages
package and extension version
shared_preload requirement
dependencies
superuser/trusted install boundary
backup/restore behavior
physical replica parity
rolling/minor/major upgrade path
license/security owner
```

四个节点能启动 core PostgreSQL，不表示某个动态库在 future promotion
candidate 上存在。package parity 应在部署时验收；catalog 中
`pg_extension` 只说明当前 database 安装了什么。

```sql
SELECT extname, extversion, extnamespace::regnamespace
FROM pg_extension
ORDER BY extname;
```

第 14 章负责扩展生命周期；本章只冻结部署前提。

### `shared_preload_libraries` 是重启边界

本章观察：

```text
pg_stat_statements, auto_explain
```

预加载库会进入 server 生命周期。新增/移除通常需要 restart，并必须在每个
可能承接 primary 的节点有兼容库。配置字符串一致还不够：

```text
package file exists
linker dependencies resolve
library matches PG major/architecture
startup succeeds
replica parity holds
```

### authentication method 与 password verifier 是两件事

`password_encryption=scram-sha-256` 控制新密码保存为什么 verifier；真正
允许哪类连接，由：

```text
listen_addresses / port / TLS
pg_hba.conf rule order
database
role
source address
auth method
client driver support
```

共同决定。

`initdb --auth-*` 会生成初始 HBA，但 Pigsty 随后声明式管理业务、复制和
管理访问。不要把 initdb 选项当作最终 access policy。

### SCRAM 迁移需要客户端矩阵

本章新环境要求：

```text
password_encryption=scram-sha-256
HBA methods use intended SCRAM policy
all drivers/pools support SCRAM
old MD5 verifier rotation is planned
```

只改 `password_encryption` 不会重写既有角色密码。role 需要重新设置密码才
生成新 verifier。不要查询或导出 `pg_authid.rolpassword` 到 evidence。

### TLS on 不是 TLS 完成

`SHOW ssl = on` 仅证明 server 可接受 TLS。完整合同还要在第 31 章验证：

```text
certificate identity/SAN
trust root
private-key permission
expiry/rotation
minimum protocol/cipher
client sslmode and hostname verification
revocation/incident process
plaintext path policy
```

本章只把 `ssl=on` 作为部署前提，不宣称传输安全审计已经完成。

### 不安全的初始化捷径

生产拒绝：

```text
initdb --auth=trust
initdb --no-sync
unknown locale inherited from shell
checksum disabled without ADR
plaintext/generated default password published in Git
mixed PG major physical replicas
extension binary only installed on current primary
```

官方文档明确把 `--no-sync` 定位为测试用途；系统崩溃可能让初始化后的目录
损坏。自动化快几秒不是持久性理由。

## 19.4.3 版本矩阵、升级窗口与勘误入口 {#item-19-4-3}

### 一条“版本号”要展开成兼容矩阵

建议基线：

| 层 | 当前身份 | 兼容/升级问题 |
|---|---|---|
| OS | Ubuntu 24.04.4 aarch64 | kernel、glibc、OpenSSL、locale |
| automation | Pigsty v4.5.0 exact tag | inventory/schema/playbook behavior |
| PostgreSQL | 18.6 | patch rollout、major upgrade |
| HA | Patroni package/version | DCS/API/config compatibility |
| DCS | etcd package/version | quorum、snapshot、client compatibility |
| proxy/pool | HAProxy/PgBouncer | protocol, auth, routing semantics |
| backup | pgBackRest + repo format | restore target/version |
| extensions | per extension | PG ABI, SQL update scripts |
| clients | driver/pool version | protocol, SCRAM, TLS, type behavior |
| observability | exporter/dashboard rules | metric name/label changes |

矩阵必须标：

```text
supported
tested
deployed
deprecated
exception
owner
next review
```

“支持 PG18”不等于“当前所有 extension build、driver 和 restore path 已在
PG18.6 测过”。

### primary 与 standby 尽量保持同一 patch

PostgreSQL
[standby planning](https://www.postgresql.org/docs/18/warm-standby.html)
指出 physical log shipping 不能跨 major，并建议尽量保持相同 release
level。短暂 rolling patch 差异需要：

- 官方升级说明；
- package availability；
- replica-first 顺序；
- rollback/forward-only 判断；
- extension parity；
- promotion eligibility；
- 限定窗口与监控。

不能把“minor 通常磁盘格式兼容”扩大成无限期混跑承诺。

### patch 与 major 使用不同 runbook

patch upgrade 常见路径：

```text
read release notes
stage package parity
upgrade replicas
restart/rejoin/observe
move service or controlled switchover
upgrade former primary
verify
```

major upgrade可能需要：

```text
pg_upgrade
logical replication
dump/restore
new cluster + migration
extension upgrade
statistics/index refresh
application compatibility
cutover/rollback boundary
```

本章只建立矩阵；[第 30 章](/version-upgrade/)执行版本升级。

### maintenance window 不只是“可以重启”

窗口要写：

```text
allowed customer impact
change start / latest abort / end
required replicas and headroom
backup/recovery prerequisite
traffic drain and connection behavior
replication catch-up limit
rollback point
owner and incident escalation
post-change observation
```

如果升级需要 40 分钟，而窗口只有 30 分钟，“尽量完成”不是计划。

### source pinning 与 dirty working tree

本章部署没有直接使用维护者本地 dirty Pigsty checkout，而是：

```text
resolve exact tag v4.5.0
record commit 2d5a45f759274048de0c197829228a71d0182e5c
git archive exact tag to private temporary directory
generate private inventory there
deploy from that archive
```

这避免把未提交修改偷偷带进正式证据。

source pinning 仍不是 supply-chain 完整方案。生产还要验证：

```text
trusted upstream/repository
tag/release artifact authenticity
artifact checksum/signature
package repository snapshot
SBOM/vulnerability response
internal promotion process
retention/rebuild ability
```

### 文档与实验也要有版本范围

书中命令页应标：

```text
last verified date
PostgreSQL major/patch
Pigsty release
OS/architecture
lab schema/release
known exceptions
```

本节验证日期是 2026-07-29。读者使用更新版本时，应先查官方 release note
和参数文档，不应因为 URL 仍可访问就假设行为不变。

### 勘误入口是一条可执行路径

发现差异时记录：

```text
claim ID
book page/anchor
observed version and platform
reproduction
expected versus actual
official source
severity
workaround
owner
target release
```

修订后要更新：

- 正文；
- lab requirements/baseline；
- validator 与 negative case；
- verified matrix/date；
- migration note。

只改一句 prose 而不改 validator，会让书和实验分叉。

### 初始化契约的 release gate

本章正式 validator 要求四个 PostgreSQL 成员都满足：

```text
server major=18
encoding=UTF8
provider=builtin
locale=C.UTF-8
checksums=on
block=8192
WAL segment=16777216
timezone=Etc/UTC
password_encryption=scram-sha-256
ssl=on
```

并运行反例：

```text
disable-data-checksums -> E_PG_INIT
```

这证明规则能拒绝一个已知错误，不只证明正常样本能被脚本读出来。

通过后的结论仍是：

```text
initialization_contract=accepted-for-this-sandbox
major_upgrade_readiness=not-tested
production_security_gate=pending
production_ch19_gate=pending
```

---

[上一节：操作系统与主机基线](../03/) · [返回本章目录](../) · [下一节：拓扑、命名与故障域](../05/) ·
[查看全书目录](/toc/) · [查看索引中心](/indexes/)
