20.2 物理流复制
PostgreSQL 物理复制不是“把表同步到另一台机器”,而是让另一套数据目录 持续接收并重放同一个 database cluster 的 WAL 历史。
理解 HA,至少要能回答:
这些问题都有 PostgreSQL 原生证据,不必靠角色标签猜。
20.2.1 WAL 发送、接收、重放与 LSN
物理复制传的是 WAL
primary 修改 data page 之前,相关变化先以 WAL record 进入 WAL。physical standby 的基本流水线:
因此“复制到达”至少有三层:
同步提交等待哪一层,由 synchronous_commit 决定;读请求能否看到,由
replay 位置决定。
LSN 是 WAL 地址
Log Sequence Number 写作:
可理解为单调推进的 WAL byte position。它不是 wall-clock time,也不是 transaction ID。
常用函数:
不要在 standby 无条件调用 pg_current_wal_lsn()。本章
ha-facts.sql 先判断 pg_is_in_recovery(),
再选择 primary 或 standby 合适的函数。
primary 观察 walsender
字段的方向:
本章每个稳定 phase 都要求 primary 看到:
state=streaming 是必要条件,不是业务 freshness 的充分条件。
standby 观察 walreceiver
每个 standby 应看到一个 receiver,且 upstream 地址是当前 primary。在正式 切换中:
这比只看 Patroni 的 Role=Replica 多证明了一层:PostgreSQL 自己确实在
接收当前 upstream。
lag 不是一个数字
至少区分:
用 byte gap 的优势是明确;局限是业务时间随 WAL generation rate 变化。
用 now() - pg_last_xact_replay_timestamp() 也有陷阱:没有新事务时,
timestamp 看起来越来越“旧”,并不代表 standby 落后。
正确做法是组合:
replay 与查询冲突
hot standby 一边 replay,一边允许只读查询。长查询可能与 recovery 需要 清理的 tuple、DDL 或锁冲突。系统必须在:
之间取舍。HA replica 若同时承担分析负载,可能让 replay lag、bloat 与 failover eligibility 互相影响。
本章把 pg-test-3 标为 offline-query placement intent,但不会把标签当成
已证明的 workload isolation。
观测快照不是连续保证
pg_stat_replication 是当前状态。采到 gap=0 只能说明采样点:
它不证明上一秒、下一秒或 primary 永久损失时也为零。本章 planned switchover 会选择健康 candidate;这正是它不能代替 catastrophic failover RPO 测试的原因。
20.2.2 timeline、恢复目标与历史分叉
promotion 会创建新 timeline
standby promotion 的本质不是“改角色字段”,而是从共同 WAL 历史的某个点 开始一条新分支。
如果旧 primary 在分支点后也继续写:
B/C 与 X/Y 可能都是各自内部合法的事务,但无法通过普通流复制自动合并。
这就是为什么 authority 与 fencing 比“选主速度”更重要。
system identifier 与 timeline
两个身份不要混:
本章正式证据:
如果成员 system identifier 不同,它不是这个 physical cluster 的合法 replica;如果 system identifier 相同但 timeline 关系不对,则必须检查 history 与分叉。
原生证据:
第二条查询有一个重要边界,后面单独解释。
timeline history
promotion 会产生 timeline history 文件,描述新 timeline 从哪条父 timeline 的哪个 WAL 位置分叉。recovery 要选择正确 history。
HA standby 通常使用:
这是 PostgreSQL 默认值,使其能跟随 promotion 后的最新历史。官方 warm standby 文档 明确建议 HA 多 standby 使用 latest。
“latest”不是允许随便选择历史。它仍依赖可达的 archive/stream、history 文件和一个被授权的 upstream。
从 WAL 文件名得到 primary 当前 timeline
WAL 文件名的前 8 个十六进制字符编码 timeline。primary 可以:
本章把它保存为:
并要求它与 Patroni 当前 timeline 一致。
不要在 recovery 中调用 pg_walfile_name(pg_current_wal_lsn());这些
current-WAL 函数不是 standby 当前 replay timeline 的通用接口。
checkpoint timeline 不是 standby 当前 replay timeline
第一次真实演练暴露了一个非常有价值的测量陷阱:
最终正式运行结束后:
没有发生“备库卡在旧 timeline”。pg_control_checkpoint() 返回 control
file 中最近 checkpoint 的信息;一个 standby 可能已经重放后续 timeline,
但还没有用新的 checkpoint metadata 更新到相同数值。
因此证据模型使用精确名称:
验收只要求:
而不是错误地要求 standby checkpoint timeline 必须时时等于 Patroni timeline。
这条经验说明:
观测函数名、状态生命周期和适用节点不清楚时,“更多 SQL”也会产生错误 告警。
如何验证 standby 跟随正确历史
组合证据:
若需要进一步诊断,可检查:
不要用一个 checkpoint 字段替代整个 lineage proof。
promotion 是不可逆状态迁移
promotion 后,新 primary 会产生新 timeline。要把它“变回原样”,不是把 配置里的 role 改回 replica:
- 若未产生分叉写入且工具能安全处理,仍需验证;
- 常见路径是
pg_rewind对齐; - rewind 前提不满足或失败时,从新 base backup 重建;
- 任何时候都必须先确认唯一的 source of truth。
本章的第二次 switchover 又创建 timeline 7;这是恢复“教学角色基线”, 不是把 WAL 历史倒回 timeline 5。
20.2.3 复制槽、归档与 WAL 保留
standby 必须拿到连续 WAL
standby 能继续 recovery 的前提:
WAL 来源可以组合:
如果旧 WAL 已在 primary 被 recycle,archive 也没有,而 standby 仍需要:
三种主要保留机制
| 机制 | 依据 | 优势 | 风险/局限 |
|---|---|---|---|
wal_keep_size |
至少保留一段量 | 简单 | 不是按 consumer 精确 |
| physical slot | 按 consumer restart LSN | 精确追踪需要 | consumer 卡住可撑满磁盘 |
| WAL archive | 外部历史 | catch-up/PITR 共用 | 需要独立完整性与恢复验证 |
它们不是互斥。一个成熟系统可能同时:
复制槽的保证与反噬
physical replication slot 告诉 primary:
原生查询:
本章每个 stable primary 都要求两个 active physical slot:
slot 名使用下划线,因为 PostgreSQL slot 名只允许特定小写字符集合。
但 slot 的保护方式是不删 WAL。如果 replica 离线很久、网络断开或 consumer 永远不回来:
官方文档明确警告这一风险,并提供 max_slot_wal_keep_size 作为边界之一。
边界达到后,slot 可能失去可继续恢复所需的 WAL,运维必须在“磁盘安全”和
“无需重建 replica”之间明确取舍。
slot 监控
至少观测:
告警不能只在 disk 95% 才触发。需要提前估算:
[ time\ to\ full
\frac{free\ bytes}{WAL\ generation\ bytes/s} ]
并考虑 burst、checkpoint、backup 与其他 slot。
archive 是另一条恢复路径
启用:
只说明 PostgreSQL 会尝试归档。还要检查:
pg_stat_archiver.failed_count 是累计量;看到历史失败不能直接判定当前坏,
也不能因为最近一次成功就忽略趋势。第 21 章会从 archive 到 restore
闭环。
slot 不是 archive,archive 不是 backup proof
slot 会随 primary 故障域一起消失;archive 若也在同一主机/账户,就可能 共同消失。
planned switchover 前检查
candidate eligibility 最低检查:
本章 executable 将 replay gap 上限固定为 1 MiB,并检查两个 sender、 两个 active slot 与 receiver upstream。这个 byte bound 只用于健康计划 切换 preflight,不等于生产 RPO。
诊断顺序
发现 replica lag:
- primary 是否继续生成大量 WAL;
- walsender
sent/write/flush/replay哪一段拉开; - standby receiver 是否 streaming、upstream 是否正确;
- network throughput/error;
- standby disk write 与 recovery apply;
- replay conflict/long query;
- slot retention 与
pg_walheadroom; - archive 是否能补缺;
- 是否已越过必须 rebuild 的 stop line。
不要先 drop slot 或删 pg_wal。“释放空间”的错误动作可能直接删除唯一
可恢复路径。
本节实验查询
在合适的节点使用:
公开实验用一个 allowlisted JSON 查询封装这些证据:
不要把复制 credential、primary_conninfo password 或完整 Patroni config
复制进 evidence。
小结
权威参考
- PostgreSQL 18:Streaming Replication、Monitoring、Slots 与 Timelines
- PostgreSQL 18:System Administration Functions
- PostgreSQL 18:
pg_replication_slots - 本章 PostgreSQL evidence query
上一节:从失败模型设计高可用 · 返回本章目录 · 下一节:同步策略与提交语义 · 查看全书目录 · 查看索引中心