31.3 从症状路由而不是猜根因
事故初期通常只能看到症状。路由的目的不是在十五分钟内宣布 root cause,而是把事件 交给一组不会立即破坏恢复条件的下一步协议。当前证据足以回答主要响应目标,就可以 先进入对应章节;新证据矛盾时再返回本节重新判型。
先填一行:
symptom what was directly observed
impact user/data/recovery consequence
known copies primary / replicas / backup / archive / clone
uncertainty writer / timeline / resource / integrity
next route PITR / HA / OVERLOAD / INTEGRITY
stop line action forbidden until which fact is known31.3.1 误删误改与恢复目标 → ch32《PITR 与误操作恢复》
什么时候进入数据恢复路线
下列证据把问题指向 ch32:
- 已提交的 DML、DDL、job 或业务事件使数据变成错误状态;
- primary 和 physical replicas 都忠实包含同一错误;
- 需要从某个历史时间、XID、LSN 或 restore point 重建旧状态;
- 当前正确写入仍在发生,不能简单让全库倒退;
- 备份与 WAL archive 是否覆盖目标边界尚需证明。
最常见的误判是“副本没延迟,所以可以提升”。物理复制正是把 WAL 中的错误
DELETE/UPDATE 重放到副本;零延迟只说明错误传播完成。
先固定三个边界
damage start 首个错误事务可能开始的边界
damage end 错误 writer 被围住或最后错误提交的边界
recovery target 期望隔离恢复停止的 time / XID / LSN / restore point告警时间通常晚于 damage start;应用日志时间还可能受时区和时钟漂移影响。优先用事务 审计、DDL log、job run id、业务 marker 和 WAL/LSN 交叉定位。PostgreSQL 连续归档可以 按 time、XID、LSN 或 named restore point 恢复,并会创建新的 timeline (PITR)。
PITR 恢复的是一个 PostgreSQL 集群状态,不是原地“撤销一条 SQL”。生产通常需要:
保留现役提交
+ 隔离恢复到目标
+ 提取受影响对象或比较 manifest
+ 处理 damage end 之后的合法写入
+ 条件化回灌或整体切换进入 ch32 前先保护输入
- 围住精确的错误 job、角色或业务写路径;
- 记录 source system identifier、timeline、当前 LSN 与目标时区;
- 确认 base backup、archive min/max、失败记录和 retention owner;
- 禁止清理 WAL、删除备份或覆盖现役主库;
- 枚举 target-only 合法提交与外部副作用;
- 指定业务数据 owner,数据库团队不能代替业务裁决旧值。
若归档覆盖未知,不要先启动一次随意恢复来“试试看”;先把现有 repo 和 WAL 保留下来, 再在独立目标验证。具体恢复目标、timeline 和回灌协议见 第 32 章。
31.3.2 主节点、复制或 DCS 异常 → ch33《故障切换与集群重建》
从四个观察面证明拓扑
“primary down”至少要拆成:
| 观察面 | 关键事实 |
|---|---|
| 客户端与服务 | DNS/VIP/HAProxy/PgBouncer 实际送到谁,旧连接还在哪 |
| PostgreSQL | pg_is_in_recovery()、system identifier、timeline、LSN、可否提交 |
| Patroni 与 DCS | member、leader lock、租约、pause/failsafe、候选状态 |
| 网络与主机 | 节点是否存活、分区方向、存储 lease/fencing 是否成立 |
四层结论不一致时,优先把它当作 writer identity 风险,而不是选择“看起来最新”的节点 提升。特别是旧 primary 与 DCS 多数派隔离时,它可能仍接受直连或遗留 VIP 流量。
PostgreSQL 核心只提供 standby、promotion 与复制机制,不负责完整的故障检测和通知;
官方 failover 文档强调旧 primary 必须有机制知道自己不再是 primary,通常称为
STONITH/fencing,以避免两个节点都认为自己可写
(PostgreSQL Failover)。
在 Pigsty 中应由 Patroni/DCS 与声明的服务机制协调,而不是绕过控制面随意
pg_ctl promote。
failover 前的最低证明
candidate system id matches; role and replay state known
data boundary receive / flush / replay LSN and expected RPO
old writer stopped or independently fenced
DCS quorum and leader ownership understood
routing exact service owner and drain behavior known
archive/slots promotion后的归档、physical/logical slot continuity assessed
rollback old primary rejoin requires rewind or rebuild, not直接开机同步复制提高已确认事务的耐久承诺,但仍要根据当时 sync_state、配置和客户端收到的
commit acknowledgment 判断;“lag 面板为 0”不是 fencing 证明。
若观察到两个 timeline 都有独占提交,事件已经同时包含数据 reconciliation。先保存两边
证据,不能用 pg_rewind 把旧分支直接覆盖。pg_rewind 官方也警告失败后的 target
数据目录可能不可恢复,应改用新备份
(pg_rewind)。
完整的候选选择、切换、fencing、旧主重建与服务验收见 第 33 章。
31.3.3 连接、延迟、CPU、内存、I/O 或磁盘表象 → ch34《过载保护与资源故障判型》
可用性故障不一定是 HA 故障
下列症状优先进入资源判型:
- connection timeout,但 primary/route 身份一致;
- pool wait、
max_connections、worker 或 thread pool 达上限; - TPS 下降伴随 lock、client、I/O、WAL flush 等 wait;
- CPU run queue、内存回收/OOM、I/O latency 或文件系统空间异常;
pg_wal、temp、日志、backup 或 telemetry retention 增长;- 单一查询、租户、job 或重试风暴占据大部分资源。
PostgreSQL 官方建议把累计统计与 ps、top、iostat、vmstat 等 OS 观察结合,
因为数据库视图不能完整描述 kernel page cache、设备和调度器
(Monitoring Database Activity)。
按资源链逐层问
admission 请求量、重试、HAProxy queue、PgBouncer wait
sessions connection slots、idle in transaction、transaction age
contention locks、buffer pin、WAL/sync、client waits
compute CPU、run queue、memory、swap、OOM、NUMA
storage latency、queue depth、throughput、filesystem/inode
persistence checkpoint、WAL generation、archive、slots、replica replay单个指标不能宣布根因。CPU 43% 时连接槽仍可耗尽;磁盘吞吐未满时 fsync tail latency
仍可拖慢 commit;ClientRead 可能表示 backend 等客户端,而不是数据库内部繁忙。
state 与 wait_event 也是独立字段,官方提醒两者之间可能出现短暂不一致。
先保住管理面和剩余容量
优先动作通常是:
- 在入口停止异常 deploy、批任务或非关键重试;
- 保留管理连接,设置诊断查询超时;
- 分批 cancel 精确 query,必要时再 terminate 已复核 session;
- 限制新工作而不是把
max_connections一路调大; - 扩容或释放已声明的应急预留,不手删数据库文件;
- 对 slot、archive、replica 和 backup 分别确认 owner 后再改变保留。
Pigsty 故障指南把数据、pg_wal、日志、本地备份、监控数据和对象存储列为不同的空间
来源
(Pigsty Troubleshooting)。先用
df 确认文件系统,再按目录类别和增长率定位;du 很重时也要限制范围和优先级。
绝不能直接删除 pg_wal 中看似旧的文件。
过载中的负载保护、连接排空、磁盘急救、内存与 I/O 判型见 第 34 章。
31.3.4 checksum、索引、排序或逻辑不一致 → ch35《数据抢救与工程取证》
先区分“不一致在哪一层”
| 证据 | 主要回答 | 不能单独证明 |
|---|---|---|
| data checksum | 读取到的物理 page 是否匹配页内 checksum | 业务值正确、所有页都已读 |
amcheck | B-tree/heap 结构的特定不变量 | 存储设备健康、业务副本等价 |
| collation version + dependency | 排序 provider 版本与依赖对象范围 | 索引已经重建、冲突值已裁决 |
| seq/index result comparison | 某查询路径是否出现差异 | 全库无其他损坏 |
| business manifest | 行数、摘要、外键、金额等业务不变量 | page/WAL 物理完好 |
| backup restore | 某份备份能恢复并读取声明对象 | 当前 primary 正确、其他时间窗可恢复 |
任何一项失败,都先记录 database、relation、fork、block、timeline、LSN、查询路径和 首次时间。任何一项通过,也只缩小它所覆盖的故障面。
修复会覆盖原因
发现 index scan 漏行时立刻 REINDEX,可能恢复服务,却同时丢掉:
- 修复前 seq/index 集合;
- 原 relfilenode 与受影响 block;
- collation 版本和升级动作顺序;
- 存储错误、checksum 与其他副本对照;
- 业务上冲突键的裁决机会。
更安全的顺序是:
围住受影响写路径
-> 保存原始存储或一致快照
-> 建立对象与业务范围映射
-> 在工作副本运行分层检测
-> 比较独立副本和备份
-> 才选择重建、提取、回灌或整体替换amcheck 提供 B-tree 检查和 verify_heapam,但不同函数的锁、代价与误报/漏报边界不同,
应按版本阅读
amcheck 文档并先在克隆测量。
pg_checksums --check 要求集群 cleanly shut down,不能在现役 primary 上临时跑
(pg_checksums)。
若一个 page 的候选来自 replica 或备份,仍不能直接复制进 live data file;WAL、tuple visibility、FSM/VM、索引和业务约束可能不在同一边界。具体的隔离、抢救、证据链和 重建协议见第 35 章。
上一节:第一原则:保护现场与可恢复性 · 返回本章目录 · 下一节:决策、沟通与变更纪律 · 查看全书目录 · 查看索引中心