跳至内容

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 known

31.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 实际送到谁,旧连接还在哪
PostgreSQLpg_is_in_recovery()、system identifier、timeline、LSN、可否提交
Patroni 与 DCSmember、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 官方建议把累计统计与 pstopiostatvmstat 等 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 等客户端,而不是数据库内部繁忙。 statewait_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业务值正确、所有页都已读
amcheckB-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 章


上一节:第一原则:保护现场与可恢复性 · 返回本章目录 · 下一节:决策、沟通与变更纪律 · 查看全书目录 · 查看索引中心

最后更新于