第 33 章 故障切换与集群重建——力挽狂澜
第 20 章演练的是健康集群上的计划切换;第 31 章要求事故处理中先保护现场、分清 事实与假设;第 32 章处理“集群健康、数据却已经写错”的 PITR。本章面对另一条恢复 路线:
原主库已经不可用或不再可信,怎样在不制造两个可写主库的前提下接受新主库,并让 旧主库安全归队?
这不是一条 failover 命令的问题。操作者必须连续回答三个不同问题:
failure diagnosis
客户端失败发生在哪一层,数据库真的失去主库了吗?
authority
哪个节点仍有权写;旧主是否已围栏;谁有资格成为候选?
lineage repair
旧主与新主是否已经分叉;可以 rewind,还是必须从可信源全量重建?把三问混在一起,会产生两类相反事故:代理故障被误判成数据库故障,执行了一次多余 promotion;真正的主机分区又被当成普通进程退出,在旧主仍可能写时接受了新主。
学习完成标准
完成本章后,读者应能:
- 区分 PostgreSQL 进程、主机、存储、复制、DCS、代理与客户端失败;
- 从用户症状反向追踪服务路径,而不是把“连不上”直接翻译成“主库宕机”;
- 用 Patroni、SQL、操作系统、DCS 与客户端证据共同判定当前数据库角色;
- 解释 WAL 的 sent、write、flush、replay 位置分别代表什么;
- 解释 timeline history、共同祖先与历史分叉,而不是把 timeline 当版本号;
- 理解“数据看起来最新”只是候选条件之一,不等于可安全提升;
- 列出 Patroni 候选的可达性、tag、lag、timeline、同步状态与 watchdog 条件;
- 区分 DCS 多数派、PostgreSQL 副本数量和业务写多数派,避免混用“quorum”;
- 解释
failsafe_mode为什么要求 incumbent primary 联系全部已知成员; - 为旧主选择进程、watchdog、云/虚拟化、电源、存储或网络 fence,并写出证据;
- 区分 automatic failover、planned switchover 与 manual failover;
- 在自动化不能证明 authority 时停下来转人工,而不是强制选一个候选;
- 判断
pg_rewind的 lineage、停机、checksums/wal_log_hints、WAL 与权限前提; - 在 rewind 失败或前提不明时,转向可信 backup 或 fresh base backup;
- 重建后复核复制槽、连接端点、客户端未知结果、监控和备份;
- 分开报告检测时间、围栏时间、控制面恢复、客户端写缺口与数据损失;
- 用 Pigsty/Patroni 命令执行动作,再回到 PostgreSQL 原生证据验收;
- 输出一份包含失败域、authority、timeline、token 对账、复位与结论边界的证据包。
三个平面,四道门
一次 HA 事故至少横跨三个平面:
| 平面 | 关键事实 | 典型证据 |
|---|---|---|
| 数据面 | 谁可写、WAL 到哪里、哪条 timeline | pg_is_in_recovery()、LSN、sender/receiver、control data |
| 控制面 | 谁持有 leader lock、谁可竞选、自动化是否暂停 | Patroni REST/CLI、DCS revision、动态配置、tags |
| 服务面 | 客户实际连到哪里、池与代理何时摘挂节点 | HAProxy/PgBouncer health、DNS/VIP、端到端 token |
恢复路径依次通过四道门:
诊断门
失败域与影响边界是否有多源证据?
围栏门
旧主是持有有效 authority,还是已经被证明不能写?
候选门
新主是否同 lineage、可达、合格且在可接受数据风险内?
交付门
路由、未知结果、归队副本、监控、归档和备份是否重新健康?任何一道门为 unknown,都不能用“业务很急”把 unknown 改写成 true。可以在事故指挥下
明确承担风险,但风险接受必须被记录,不能藏在 --force 后面。
本章的核心不变量
故障切换不是“副本变成主库”,而是维持下面的不变量:
$$ \left|\text{accepted writable primary}\right| = 1 $$
这里的 accepted 很重要。网络分区两侧可能各有一个 pg_is_in_recovery()=false 的
PostgreSQL;只看本机 SQL 会得到两个“主库”。要让其中一个可被系统接受,还必须有
authority 与 fence:
accepted primary
= writable PostgreSQL
+ valid control-plane authority
+ old-primary exclusion
+ admissible lineage
+ service-route acceptance路由摘除只能防止正常客户端访问旧主,不等于围栏。定时任务、直连、复制、维护脚本或 网络另一侧仍可能写入旧主。真正的 fence 必须使它失去写能力,或至少使它无法继续产生 会被业务接受的状态,并能由独立证据验证。
正式实验
本章在已确认的四节点 Pigsty 开发沙箱完成两段真实实验。
第一段对 managed pg-test:
initial primary pg-test-1
eligible replicas pg-test-2, pg-test-3
fault guarded systemctl stop patroni on pg-test-1
candidate not forced; selected by Patroni at runtime
client 200 ms idempotent INSERT through port 5433
rejoin start pg-test-1 and require streaming
baseline restore planned switchover to pg-test-1
DCS/network mutation none
managed reinit none实际候选是 pg-test-3。这不是脚本预期之外的杂音,而是实验最重要的结果之一:
自动竞选必须接受“任一满足条件的副本”,runbook 不能把某个候选预写成已经发生的事实。
正式观测:
timeline 17 -> 18 -> 19
Patroni service stop 1.582 s
action start -> process fence 1.817 s
action start -> topology stable 4.536 s
old primary start -> streaming 2.527 s
planned baseline switchover 2.832 s
client attempts 160
acknowledged 130
unknown 30
acknowledged missing 0
duplicate tokens 0
unreconciled unknown 0
maximum acknowledgement gap 6.212 s服务路径通过 Unix socket 回源,inet_server_addr() 为 NULL,因此 runner 没有伪造
后端地址,而是用“客户端观察到的 timeline + 同期 Patroni 拓扑”归属提交:旧 timeline
确认 18 次,新 timeline 确认 112 次。
第二段在 pg-test-3 的一次性目录创建同源临时集群 A/B/C:
A -> basebackup -> B
stop A
promote B and write new-primary branch
stop B
start A alone and write old-primary-divergent branch
stop A; restart B
pg_rewind A from B -R
start A as streaming standby
fresh pg_basebackup C -R from B
start C as streaming standby
stop all and remove exact root两个分叉 primary 从不同时运行。结果:
same system identifier true
timeline diverged true
pg_rewind 0.245 s
rewound A streaming true
B branch markers on A present
A divergent marker after rewind absent
fresh pg_basebackup 0.228 s
C streaming true
temporary root removed true这些是几十 MB 的本机虚拟化沙箱观测,不是生产 RTO。环境使用异步复制、单 etcd、
watchdog off;没有注入断电、存储故障、真实网络分区,也没有执行破坏性的 managed
reinit。最终门禁保持 production_ch33_gate=pending。
所属位置
- 卷别:下卷:运维管理(独立导读页,不构成章节父目录)
- 教学分组:第六篇:出山——按响应目标演练恢复与改进
- 前置:第 20 章 高可用拓扑与容灾目标、 第 31 章 事件分级、现场保护与应急决策
- 后续:第 34 章 过载保护与资源故障判型
- 兼容入口:
/ch33/、/volume-2/failover-rebuild/
本章目录
33.1 先识别失败域
33.2 复制状态与时间线证据
33.3 自动故障转移的保护条件
33.4 DCS 故障的安全处理
33.5 旧主重加入与集群重建
33.6 切换与重建 runbook
33.7 实战:主库故障与 DCS 干扰
权威参考
PostgreSQL:
Patroni:
Pigsty:
上一章:PITR 与误操作恢复——妙手回春 · 返回下卷导读 · 下一章:过载保护与资源故障判型——李代桃僵 · 查看全书目录 · 查看索引中心