跳转到主要内容

31 事件分级、现场保护与应急决策——枕戈待旦

前十二章已经建立部署、HA、备份、安全、可观测、容量、调优、维护、迁移与升级能力。 系统真正出事时,这些能力不会自动拼成一次正确响应:告警只呈现某个观察面的症状, 自动化可能继续改变拓扑,人在压力下又容易把第一个解释当成根因。

本章是第 32~35 章的共同控制平面。它不提前穷举所有故障,而是先回答四个问题:

现在影响了谁,数据与恢复能力是否仍安全?
哪些状态仍在变化,哪些自动化需要精确暂停?
已有证据把问题路由到哪类恢复目标?
下一步动作由谁执行,何时停止,怎样证明结果?

事故早期最稀缺的通常不是命令,而是可信状态与可逆选择。好的响应不以“最快猜到 根因”为目标,而以更快降低用户影响、数据风险和不确定性,同时保留恢复路径为目标。

学习完成标准

完成本章后,读者应能:

  1. 从用户影响、数据风险、范围、变化速度和可恢复性分级事件;
  2. 把“恢复数据、恢复拓扑、释放压力、保护完整性”写成明确响应目标;
  3. 区分严重度与技术判型,不让首发告警直接授权重启、提升或删除;
  4. 有选择地控制 Patroni、调度器、路由与保留任务,而不是笼统“暂停一切”;
  5. 采集带 UTC、拓扑、版本、system identifier、timeline、LSN 与来源散列的最小证据;
  6. 按 PITR、HA、过载和完整性四条路线进入第 32~35 章;
  7. 用“事实—假设—动作—预期—停止线—回退—结果”记录每次决策;
  8. 在单人和团队两种模式下完成前十五分钟响应与可读交接;
  9. 识别必须引入业务、存储、安全、法务或厂商的升级条件;
  10. 组织一次不触碰生产的盲抽桌面演练,并区分工具通过与人员胜任。

一张图看懂事故控制面

detect symptom
  -> declare incident and start UTC timeline
      -> quantify impact / data risk / scope / trend / recoverability
          -> preserve writer identity / WAL / backup / evidence
              -> collect independent layers
                  -> choose PITR / HA / OVERLOAD / INTEGRITY
                      -> authorize one bounded action
                          -> verify actual result
                              -> communicate and hand off

若新证据推翻当前路线,应回到 triage;若动作越过了写入、timeline 或不可逆边界,应更新 回退定义。严重度可以升降,技术路线也可以改变,但每次改变都必须留下事实和时间。

本章目录

31.1 事件分级与响应目标

31.2 第一原则:保护现场与可恢复性

31.3 从症状路由而不是猜根因

31.4 决策、沟通与变更纪律

31.5 单人值守与团队响应

31.6 实战:盲抽症状的桌面演练

写作与验收提示

本章提供八个盲抽场景,每条后续技术路线各两个。正式参考 run 在 Pigsty FULL/L3 监控环境中对 pg-testL0-read-only 采集,再离线完成一份 solo 与一份 team 响应:

PostgreSQL                         18.6
Patroni topology                   1 primary + 2 replicas
timeline                           11
pgBackRest status / backups        0 / 6
drawn routes                       INTEGRITY + PITR
online mutation                    none
real incident injected             false
dangerous actions executed         0

验证器拒绝了 31 个声明反例和 18 个现场证据变异,并绑定 12 个实验源文件。公开摘要见 incident-run.json,完整边界见 lab-contract.md

这次通过只证明只读采集、盲包、响应合同和校验器可执行;参考响应由程序生成,不代表 任何真人通过了能力考核,更没有实施 failover、恢复备份或处理真实事故。 production_ch31_gate 保持 pending


上一章:推陈出新:版本升级与回滚策略 · 返回下卷导读 · 下一章:PITR 与误操作恢复——妙手回春 · 查看全书目录 · 查看索引中心

31.1 事件分级与响应目标

监控系统每天会产生很多 event,真正需要进入 incident response 的只是其中一部分。 本书把“事件”定义为:用户、数据、恢复能力或关键控制面出现现实风险,需要超出日常 工单节奏的协调、记录和处置。它可以由告警触发,也可以由用户投诉、审计差异、存储 报错或一次危险变更触发。

这个定义故意不要求先知道根因。先建立响应节奏,才有机会在状态继续变化之前保存证据 和恢复选择。NIST SP 800-61r3 也把检测、响应、恢复放进持续的风险管理活动,而不是把 响应理解成一次孤立的“修服务器”任务 (NIST SP 800-61r3)。

31.1.1 用户影响、数据风险、范围与持续时间

用五个轴描述事件

“数据库有问题”不是可以分级的事实。事件声明至少要填五个轴:

要回答的问题 可验证的例子
用户影响 谁现在不能完成什么? checkout 写失败 74%,只读目录正常
数据风险 已提交状态会丢、错、重复或泄漏吗? 12,400 行被错误更新,外部退款 318 笔
blast radius 哪些租户、库、表、节点、地域和依赖受影响? pg-test 写服务;只涉及订单库
time dynamics 稳定、扩散、振荡还是已停止? WAL 每分钟增长 3 GiB;错误 job 仍运行
recoverability 现有副本、WAL、备份和证据还能支持什么? archive 覆盖事发前 41 小时,尚未试恢复

每个结论都应带三个限定:

as_of       2026-07-30T02:14:00Z
source      dashboard / SQL result / ticket / business owner
confidence  observed / inferred / unknown

不要把 unknown 填成 zero。没有发现数据损坏,可能只是还没有做 manifest 或 checksum 验证;监控图没有错误,也可能是 exporter、规则或标签链路已经失效。第 25 章建立的 “signal missing 也是状态”在事故中尤其重要。

严重度是一套组织合同

下面是一种可用的起点,不是 PostgreSQL 的内置标准:

等级 典型触发 协调节奏
SEV1 核心服务大面积不可用;数据完整性或唯一 writer 不确定 立即指挥、持续记录、5~15 分钟更新
SEV2 重要功能显著受损;范围受限但可能扩大 值班负责人接管、15~30 分钟更新
SEV3 局部降级且有可靠绕行,数据风险低 工作时段协调并持续观察
SEV4 无当前用户影响的缺陷或近失事件 正常问题管理

真正的阈值必须写入组织策略:多少用户、多少收入、哪类数据、哪个合规义务。数据库 SLO 可以帮助衡量可用性,却不能单独覆盖错误数据、隐私泄露或恢复窗口丢失。

分级不是一次性动作。影响扩大、发现第二个 writer 或确认 archive 断档时要升级;入口 恢复而数据仍不可信时,不能因为 HTTP 200 回来了就降级。

31.1.2 恢复数据、恢复拓扑、释放流量压力、抢救完整性

先说要恢复什么

restart PostgreSQLpromote replicadrop slot 都是动作,不是目标。若没有目标, 团队无法判断动作成功后是否真的改善了局面。第六篇把主要响应目标分成四类:

目标 典型问题 第一优先保护 后续章节
恢复数据 已提交数据被误删、误改或要回到历史边界 writer、WAL/archive、恢复目标 ch32
恢复拓扑 primary、timeline、DCS 或服务路由不确定 唯一 writer、fencing、提交边界 ch33
释放压力 连接、锁、CPU、内存、I/O、磁盘成为约束 管理通道、关键流量、剩余容量 ch34
抢救完整性 page、checksum、WAL、index 或业务等价存疑 原始介质、独立副本、证据来源 ch35

一次事件可能同时有多个问题。例如 inactive slot 填满 pg_wal,既威胁可用性,也威胁 恢复能力。此时主要目标可以先定为“释放压力,但禁止直接删除 WAL”;当空间稳定后再 转入 consumer 重建与完整性验证。

响应目标必须可验收

把“尽快恢复”改写成状态断言:

用户状态       关键写接口恢复,错误率低于已声明阈值
数据状态       marker 边界后的 manifest 与业务不变量成立
拓扑状态       一个可写 primary,所有服务端点与 DCS 认知一致
恢复状态       新 WAL 持续归档,备份覆盖未被破坏
证据状态       时间线、动作与结果均有来源和散列

这些状态可以分步达到。为了保护数据,允许先进入 degraded read-only;为了避免满盘, 可以先 shed 非关键写,而不是立即恢复所有流量。关键是把“临时稳定状态”和“最终恢复 状态”分别命名,避免临时措施永久化。

最小化不可逆性

在信息不足时,优先级通常是:

围住继续扩散
  -> 保存恢复与取证输入
      -> 建立独立观察
          -> 执行最小范围的可逆动作
              -> 验证后再扩大

这不是“永远不动作”。磁盘还剩两分钟时必须行动,但动作仍应有精确目标、owner、停止线 和后续代价。例如扩容比删除 pg_wal 更可控;暂停一个错误 job 比停止整个集群更容易 验证;隔离恢复比在主库上直接覆盖更能保留选择。

31.1.3 严重度决定节奏,不替代技术判型

相同症状,可以是四条完全不同的路

“应用连不上数据库”至少可能表示:

错误 DDL/DML 后应用主动拒绝服务       -> PITR / data recovery
HAProxy health check 失效              -> HA / service topology
max_connections 或 pool queue 耗尽     -> overload
关键 catalog/page 无法读取导致启动失败  -> integrity

它们都可能是 SEV1,但安全动作相反。第二种情况下随机提升副本会把接入故障变成双主; 第三种情况下重启只会暂时清空连接,重试风暴会再次打满;第四种情况下在原介质上反复 启动可能覆盖证据。

因此,严重度只控制:

  • 谁必须加入、谁有决策权;
  • 更新频率和业务沟通范围;
  • 可以接受多大的临时降级;
  • 多快需要引入外部专家。

它不告诉你根因,也不降低高风险动作的证据门槛。

把第一个解释当作假设

首发告警应写成:

fact        write endpoint returned 503 from 02:03Z
hypothesis  primary may be unavailable
alternatives
            proxy health check / pool exhaustion / DCS partition /
            primary crash / network path
next test   compare endpoint, Patroni, SQL role and DCS leader

一次测试的价值不只在“证实”,也在排除。若直连现任 primary 成功,不能直接宣告数据库 健康;它只降低了“postmaster 已停止”的可能性,还要检查服务 backend、writer 唯一性 和提交确认。

两条状态线并行更新

事故记录中应分别维护:

impact line      SEV1 -> SEV2 -> resolved
technical route HA -> OVERLOAD -> recovery complete

入口恢复可能让影响下降,但若数据等价尚未验证,技术事件仍未关闭。相反,根因尚未知时 也可以通过限流、围栏或只读模式降低影响。把两条线分开,团队才不会为了“找到根因” 延误止血,也不会为了“服务绿了”过早结束调查。


返回本章目录 · 下一节:第一原则:保护现场与可恢复性 · 查看全书目录 · 查看索引中心

31.2 第一原则:保护现场与可恢复性

事故现场不是静止的。应用在重试,Patroni 在判断角色,WAL 在产生和回收,日志在轮转, backup retention 在清理历史,运维人员的查询本身也会留下连接和日志。所谓“保护现场” 不是把所有东西停住,而是知道谁还会改变什么,有选择地围住最危险的状态转移,并 为恢复保留必要的 WAL、备份、拓扑和业务边界。

31.2.1 暂停自动化、危险变更与证据覆盖

先列控制器,再决定是否暂停

Pigsty 集群中常见的主动控制器包括:

控制器 可能继续做什么 盲目停止的代价
Patroni + DCS 角色管理、重启、failover、配置协调 丧失自动保护;pause 也不是 fencing
HAProxy / VIP / pool 根据健康检查改变流量去向 现有连接与新连接可能走不同节点
应用重试与 job 继续 DML、DDL、回填、消费消息 错误或负载继续扩大
WAL archiver / backup 保存恢复链、执行保留策略 停 archive 可能填满 pg_wal 并缩短恢复窗口
autovacuum / maintenance 清理 tuple、冻结 XID、重建对象 全局停用会引入膨胀和 wraparound 风险
日志轮转与 telemetry retention 覆盖旧日志、聚合或丢弃高基数字段 取证窗口变短;全部开启 debug 又可能泄密或打满盘

正确的“冻结记录”应逐项写:

controller       refund-consumer / Patroni / backup retention
observed_state   running, config hash abc..., owner team-x
exact_scope      run_id=backfill-20260730 only
reason           stop new external side effects
expected_effect  queue remains retained; other consumers continue
resume_owner     business owner
resume_condition reconciliation manifest approved

优先围住制造新歧义的 writer、重试和错误 job。不要为了“干净现场”关闭 WAL 归档、删除 旧备份或把所有监控改成 debug;这些动作可能同时破坏恢复能力和磁盘余量。

Patroni pause 不是“时间停止”

Patroni 的 pause mode 适合大版本升级、损坏恢复等需要暂时脱离自动管理的特殊操作,但 其官方语义很具体:

  • member key 和 primary 的 leader lock 仍会更新;
  • Patroni 仍可能对运行中的成员做只读查询;
  • 人工 restart、failover/switchover 和 reinitialize 仍可执行;
  • 发现 parallel primaries 时只告警,并不会自动 demote 无 leader lock 的 primary;
  • PostgreSQL 停止后不会被自动拉起。

所以 patronictl pause 既不是写围栏,也不是禁止所有人工动作。使用前必须先记录成员、 leader、timeline、路由和现有 pause 状态,明确谁负责 resume,并验证旧 writer 已在 网络、存储或服务层被独立围住。单纯停止 Patroni 进程更不是维护模式。

暂停要有过期条件

每个临时控制都要有:

start UTC
owner
exact target
automatic expiry or review time
health signal while paused
resume command and verifier

没有恢复条件的临时限流会变成永久容量损失;遗忘的 Patroni pause 会让后续故障不再 自动恢复;遗忘的 archive/retention 变更会在几小时后制造第二次事故。事件结束条件中 必须包含“所有临时控制已复位或进入有 owner 的计划变更”。

31.2.2 记录时间、拓扑、版本、告警与最近变更

建立 incident envelope

第一份现场包不需要“把服务器全抄走”,但必须能回答:

incident id / collector / UTC / clock source
environment / cluster / service endpoint
PostgreSQL version / system identifier / timeline / LSN
primary and replica observations / Patroni and DCS state
HAProxy / pool / VIP route
backup stanza / latest backup / archive boundary
recent deploy / DDL / config / secret / storage / network change
active user impact and business marker
every artifact's source, collection command, hash and access class

system_identifier 区分不同 PostgreSQL 集群,timeline 识别 failover 后的历史分支,LSN 描述同一 timeline 上的位置;三者不能互相替代。连接到“一个叫 production 的端点” 并不能证明取到了预期数据副本。

运行中的 PostgreSQL 可以在短超时只读事务中取 control identity:

\set ON_ERROR_STOP on
SET statement_timeout = '5s';
SET lock_timeout = '500ms';
SET default_transaction_read_only = on;

BEGIN READ ONLY;
SELECT
  clock_timestamp() AS observed_at,
  current_setting('cluster_name') AS cluster_name,
  current_setting('server_version') AS server_version,
  pg_is_in_recovery() AS in_recovery,
  s.system_identifier,
  c.timeline_id,
  c.checkpoint_lsn,
  c.redo_lsn,
  c.checkpoint_time
FROM pg_control_system() AS s
CROSS JOIN pg_control_checkpoint() AS c;
COMMIT;

离线数据目录或只读取证副本可以使用 pg_controldata,但报告中 必须写清楚读取的是哪个路径、当时是否运行、工具版本和文件来源,不能把两个节点的 输出拼成一条时间线。

动态状态、累计统计和日志不是同一种证据

PostgreSQL pg_stat_activity 描述当前 backend;pg_stat_replication 描述当前 walsender;pg_stat_databasepg_stat_archiver 等是累计计数。官方文档指出累计统计不会瞬时更新,并且默认在同一 事务内缓存;clean shutdown 可以保存统计,而 crash、base backup 启动和 PITR 会重置 累计计数 (Cumulative Statistics System)。

因此:

  • failed_count = 21 不等于当前 archive 正在失败,要比较 reset、last failure、 last success 和当前 WAL;
  • deadlocks = 0 必须同时记录 stats_reset
  • 一张事务内的静态快照适合关联字段,连续变化率则要用多个有时间的样本;
  • 采集活动时默认按 state、wait 和 age 聚合,原始 SQL、bind value、client address 只在必要且获授权时进入受控证据。

PostgreSQL 日志还有轮转与覆盖策略;log_truncate_on_rotation 在特定文件命名下可以 周期性覆盖旧内容 (PostgreSQL Logging)。 应先复制事故时间窗内的精确文件并计算散列,而不是先改 logging 配置或执行一次会刷屏 的诊断。

在 Pigsty 中跨层对齐

Pigsty 的 FULL/L3 监控把 PostgreSQL、PgBouncer、Patroni、HAProxy、主机与日志用 clsinsip 等标签关联。事故时可以从 PGSQL Alert → Cluster → Service / Patroni / Replication / Persist → Instance / Session / Query 逐层缩小,而不是只看 一张总览 (Pigsty Dashboard)。

命令行可以用:

pig context -o json

获取主机、PostgreSQL、Patroni、pgBackRest 与扩展上下文 (pig context)。把它当采集起点而非唯一 真相:还要用 SQL role、Patroni REST、DCS 和客户端端点交叉验证。配置和 secret 文件 通常只保存版本号、权限和 SHA-256,不把内容直接放进普通事件频道。

证据清单本身也要可审计

artifact       patroni-members.json
observed_at    2026-07-30T02:05:03.441Z
collector      oncall-a
source         three declared REST endpoints
scope          state / role / version / timeline only
sha256         ...
redaction      connection_url and tags removed
access         incident-restricted

NIST SP 800-61r3 要求保留 incident data 的完整性和 provenance,同时保护响应记录的 机密性。散列只能证明“以后看到的 bytes 没变”,不能证明采集命令正确、时钟准确或源 本身可信;这些信息要由 provenance 补齐。

31.2.3 先克隆、隔离或只读,再做破坏性尝试

保存原件,实验只在工作副本

可能改写数据页、WAL、catalog 或时间线的动作,先问:

原始状态是否已有独立副本?
这个副本在什么一致性边界创建?
数据目录、tablespace 和 WAL 是否属于同一快照?
工作副本是否与生产网络和路由隔离?
若动作失败,原始副本和恢复链是否仍可用?

推荐分成三份:

副本 目的 允许动作
原始证据 保留最早可得状态 不直接启动或修复;受控访问
工作副本 执行 amcheck、WAL 分析、恢复和修复试验 可销毁、可重新生成
恢复候选 通过验证后准备回灌或接管 只执行 runbook 声明动作

“把目录复制了一份”并不自动得到有效 physical backup。运行中对 PGDATA 做普通文件复制 可能跨越不同页和 WAL 时刻;带 tablespace 的集群还可能跨多个文件系统。使用 pgBackRest、PostgreSQL backup API 或由存储平台保证一致性的原子快照,并记录其 crash-consistent / application-consistent 语义。

副本不等于历史

物理 replica 会重放主库已经提交的错误 DELETE,不能当成误操作前的时间胶囊; 共享故障域中的存储损坏也可能影响多个副本。每份候选都要独立标注:

system identifier
timeline and fork point
last replayed / checkpoint LSN
capture time and clock
checksum state
backup and archive ancestry
business manifest

只有这些边界与恢复目标匹配,副本才有资格成为数据来源。

“只读”有多个层次

  • SQL BEGIN READ ONLY 阻止普通数据库写事务,但不冻结其他会话、WAL replay 或后台 状态变化;
  • 只读业务路由只约束经过该端点的应用,不约束 owner、scheduler 或直连;
  • 文件系统只读保护 bytes,却可能使 PostgreSQL 无法完成 crash recovery,不能在 原始取证挂载上强行启动;
  • snapshot clone 通常是独立可写工作副本,但 CoW 底层和保留策略仍要记录。

因此应保存不可变原件,再从它派生可写工作副本,而不是把“只读”当作万能开关。

pg_waldump主要用于 debug 和教学, 官方还提醒在 server 运行时可能给出错误结果。对事故 WAL 的分析应固定工具版本、 timeline 和 segment 范围,优先在复制出的 WAL 上完成;不要为了让工具读取而随意改名 或移动现役 pg_wal 文件。

破坏性尝试必须留下退出线

下列动作默认只在工作副本:

zero_damaged_pages
pg_resetwal
手工 page/file copy
强制 catalog 修改
无来源约束的 REINDEX / VACUUM FULL
覆盖式 PITR
失败后继续使用同一个 pg_rewind target

即便工作副本“修好了”,也要再回答:丢了哪些 tuple、违反了哪些业务不变量、能否从 WAL/备份/其他副本补齐、修复步骤能否重复、生产切换后如何对账。能启动只是一条证据, 不是完整性结论。


上一节:事件分级与响应目标 · 返回本章目录 · 下一节:从症状路由而不是猜根因 · 查看全书目录 · 查看索引中心

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 实际送到谁,旧连接还在哪
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 官方建议把累计统计与 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 业务值正确、所有页都已读
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 章


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

31.4 决策、沟通与变更纪律

故障不会因为开了 incident channel 就停止变化。没有统一决策格式时,聊天记录很快会 混合事实、猜测、建议、已经执行的命令和转述结果;十分钟后,团队甚至无法回答“谁在 哪台机器上改了什么”。响应纪律的作用不是增加仪式,而是让并行思考最后汇聚成一个 可审计的系统状态转移。

31.4.1 事实、假设、动作、预期与停止条件

五类记录不能混写

类型 含义 示例
fact 有来源、时间和范围的直接观察 02:03Z 写端点 503;直连 primary 只读探针成功
hypothesis 对事实的可证伪解释 HAProxy health credential 可能失效
decision 在不确定性下选择目标或约束 在证明 writer 唯一前禁止 promote
action 获授权并实际执行的状态改变或测试 回退 exact health-check secret version
result 动作后重新观察到的事实 三个 backend 中仅 DCS leader healthy

“Patroni 有问题,已处理”不属于任何合格类型。它没有时间、来源、动作和结果,也让接班 人无法判断现在是否安全。

把每个动作写成有界实验

一次可执行动作至少包含:

minute / UTC
actor and exact target
facts and hypothesis that justify it
risk class and approver
rendered action
expected observable result
time budget
stop condition
rollback or compensation
actual result and evidence id

例如:

fact       HAProxy has zero healthy backends; all Patroni members agree
            pg-test-1 is primary on timeline 11
hypothesis health-check secret v42 is invalid
action     access owner rolls back only secret v42 -> v41
expected   pg-test-1 becomes healthy within 10 s; no role change
stop       any replica becomes write backend, or primary identity changes
rollback   restore v42 and keep write endpoint fenced
result     pending

预期结果必须是可以再观察的状态,不是“应该修好”。停止条件写在命令之前,因为执行后 人容易被 sunk cost 推着继续。没有可行 rollback 时,要明确写 irreversible after <boundary>,而不是填一个虚假的“恢复备份”。

时间线记录的是认识变化

02:03 fact       endpoint 503
02:05 hypothesis primary may be down
02:07 fact       SQL primary running; Patroni/DCS agree
02:08 hypothesis access health check failed
02:10 decision   hold failover; access rollback authorized
02:11 action     health credential v42 -> v41
02:12 result     backend healthy; canary commit succeeds
02:14 decision   impact downgraded; data validation remains open

早期假设错误并不可耻,偷偷改写历史才危险。保留被推翻的假设和证据,可以解释为什么 没有执行另一条路径,也能在复盘中发现告警或 runbook 的诱导性。

所有时间以 UTC 为主,并记录 collector clock。若应用、主机和数据库时钟有偏差,先 保存各自时间再估算 offset,不要直接修改原始日志时间。

31.4.2 单一指挥、记录员、执行者与业务接口

单一指挥不等于单一思考

团队模式至少有四个逻辑角色:

角色 负责 不负责
incident commander 目标、优先级、风险门、角色与更新节奏 亲自解释所有技术细节
operator 预检、读回目标、执行一个获批动作、报告原始结果 私自扩大范围或并行试命令
scribe UTC 时间线、证据 ID、决定、结果和待办 把聊天摘要伪装成事实
business liaison 用户影响、业务优先级、外部副作用和状态更新 替数据库团队选择技术命令

还可以增加 HA、storage、security、application 等 subject-matter expert。NIST SP 800-61r3 强调现代响应依赖领导、事件处理者、技术人员、法律、公共沟通和资产 owner 等多方参与;这些角色要由一个协调实体汇合,而不是各自行动。

IC 不必是职位最高或 PostgreSQL 最熟的人,而应能维护共享状态、拒绝未经验证的高风险 动作、召集正确 owner。技术负责人可以给出候选方案,最终由 IC 根据业务目标和授权 选择状态转移。

一次只允许一个命令 owner

可以并行:

  • 一组读取 Patroni/DCS;
  • 一组确认应用影响和最近变更;
  • 一组检查 backup/archive;
  • scribe 持续整理时间线。

不能并行:

  • 两个人同时修改同一 cluster;
  • 一边 failover,一边重启旧 primary;
  • 一边 drop slot,一边尝试恢复 consumer;
  • 一边回切路由,一边解除写围栏。

每个有副作用的系统在一个时刻只有一个 operator token。执行前读回:

incident / environment / cluster / node
current role / system id / timeline
exact command or API request
expected / timeout / stop / rollback
approver

执行者贴回 exit status、时间与证据引用,不只说“done”。讨论频道、命令频道和业务状态 频道最好分开,避免建议被误当命令、原始日志被转发给无权限人员。

对外更新只说已知边界

一条合格更新包含:

known       checkout writes degraded since 02:03Z
unknown     final cause and whether 318 requests have external side effects
impact      74% write failure; reads unaffected
doing       bad deploy fenced; recovery and topology evidence being checked
next update 02:20Z, or earlier on material change

不要为了显得确定而宣布未经验证的 root cause 或恢复时间;也不要把技术日志直接扔给 业务方。固定下一次更新时间可以减少无序追问,让 operator 保持注意力。

31.4.3 高风险动作的复核、审批与回退

风险分级针对动作,不针对职位

级别 例子 最低控制
R0 观察 短超时聚合 SQL、只读 REST、配置 hash 范围、来源、timeout
R1 可逆 containment 限制一个 job、摘掉一类非关键流量 exact target、owner、验证、恢复条件
R2 受控状态变更 terminate 精确会话、有限且可回退的路由调整、drop exact rebuildable slot、隔离 failover 演练 IC 批准、独立技术复核、回退/补偿
R3 生产敏感/潜在不可逆 生产 failover 或 authority cutover、覆盖恢复、rewind、pg_resetwal、手工页修复 保存原件、明确授权、双人 command review、停止线

熟练 DBA 执行 R3 仍然是 R3。SEV1 可以缩短等待,但不能让 system identifier、 timeline、目标路径和 writer fencing 变得不重要。

批准之前解析最终目标

审批材料不能只有模板变量:

bad:  drop slot ${SLOT}
good: cluster=pg-prod, system_id=..., primary=pg-prod-2,
      slot=cdc_legacy, active=false, retained=1.31TiB,
      consumer owner=..., rebuild approved=..., command hash=...

最终执行包应包含:

  1. 当前证据与目标状态;
  2. exact host/service/database/object/PID/path;
  3. 命令渲染结果,而不是未解析变量、glob 或 command substitution;
  4. 前置条件和权限;
  5. 预计持续时间、负载与观察查询;
  6. stop condition;
  7. rollback、compensation 或不可逆边界;
  8. operator、reviewer 与 IC 的时间戳。

审批证明组织授权,不证明命令技术上正确。reviewer 必须独立检查目标、方向和恢复前提, 不能只回一个“LGTM”。

回退、补偿和重建不是同义词

类型 含义 例子
rollback 恢复原状态且没有新独占事实 目标写入前切回 copy-mode 旧集群
compensation 原动作无法抹去,新增反向业务动作 已发退款后执行会计冲正
rebuild 丢弃一个非权威副本,从权威来源重建 failover 后重建旧 primary 为 replica

DROP replication slot 无法 rollback;只能让 consumer 从新快照重建。目标数据库已接受 独占写后,切回旧库需要反向对账,不再是路由 rollback。pg_rewind 失败后不能假设重跑 会恢复 target;官方建议此时取新备份。

高风险动作完成后必须运行独立的 verifier。执行命令的人报告成功还不够:服务 owner 验证端点,数据 owner 验证业务 manifest,HA owner 验证 writer/timeline,backup owner 验证归档与恢复链。最后再决定扩大流量或进入观察窗口。


上一节:从症状路由而不是猜根因 · 返回本章目录 · 下一节:单人值守与团队响应 · 查看全书目录 · 查看索引中心

31.5 单人值守与团队响应

同一响应协议既要能被凌晨单人值守执行,也要能在几十人加入时保持一致。差别不在技术 正确性:单人把角色按时间串行切换,团队把只读调查并行化;两种模式都要有一个状态、 一个时间线、一个命令 owner 和明确的升级条件。

31.5.1 单人时先稳定、记录,再逐级升级

前十五分钟按顺序切换角色

分钟 逻辑角色 产物
0~2 IC incident ID、UTC 起点、当前用户影响、下一次更新时间
2~5 scribe 首发事实、告警来源、最近变更、当前自动化和 writer 身份
5~9 operator L0 只读现场包;连接/HA/资源/完整性最小证据
9~12 technical lead 候选路线、被排除路线、第一安全动作与 stop line
12~15 liaison 呼叫所需 owner、发布 known/unknown/impact/next update

一个人可以戴四顶帽子,但要按顺序。先把“准备执行什么”写进时间线,再切到 shell; 执行后贴原始 exit status 和 verifier 结果,再回到记录。多开几个终端不会让一个人获得 真正的独立复核,只会增加连错环境和忘记动作的概率。

单人第一目标是保持可操作

  • 保留一个管理连接或 out-of-band 路径;
  • 给诊断 SQL、SSH 和 HTTP probe 设置短 timeout;
  • 先停止精确的错误来源,不做全局“重启看看”;
  • 把当前 cluster、node、role、system id、timeline 放在命令前;
  • 不在普通聊天粘贴连接串、原始 SQL、业务行或私钥;
  • 每五分钟停一次,问“现有动作是否仍匹配响应目标”。

若手已经在高压下连续操作,最有价值的动作往往是叫醒第二个人。独立读回一个 target 可以阻止方向反了的 failover、误删 slot 或在错误 data directory 上恢复。

预先写死升级触发器

单人不能因为“还没完全搞清楚”而无限延迟呼叫。下列任一项应立即升级:

writer uniqueness unknown
checksum / WAL / page / storage media error
backup or archive recovery window unknown
R2/R3 action required
regulated or financial data involved
credentials / intrusion / malicious change suspected
impact still growing after first containment
fifteen minutes内没有形成可证伪路线

真正存在“几分钟后满盘”的倒计时,也只授权最小保护动作,例如按声明 marker 释放 emergency reserve 或在入口 shed load;它不自动授权删除 pg_wal。按组织 break-glass 政策执行后,必须立刻补齐 owner、证据和独立复核。

防范单人认知陷阱

陷阱 自检问题
anchoring 除首发告警外,还有哪两个解释?
action bias 不做这条命令,未来五分钟会丢掉什么?
confirmation bias 哪条最便宜的证据会推翻我的路线?
sunk cost stop condition 是否已经触发?
fatigue 我能否准确复述 target、方向和不可逆边界?

如果最后一个问题答不清,应停止有副作用的操作并交接,而不是靠咖啡继续。

31.5.2 团队时避免多人同时改同一系统

两分钟内建立响应拓扑

IC                 one person
database operator  one active command token
scribe             one canonical timeline
business liaison   one outward status owner
read-only tracks   access / HA / database / host-storage / backup

人员不足时可以合并角色,人员过多时也不要复制角色。新加入者先读当前摘要和 stop line, 再领取一个问题;不要从头重复所有探针或提出第五次“要不要重启”。

并行问题,不并行状态改变

一个好的分工可能是:

track A  从客户端、HAProxy、PgBouncer 还原实际路径
track B  读取 Patroni、DCS、PostgreSQL role/timeline
track C  检查 host resource、storage 和最近 kernel error
track D  检查 backup/archive/recovery coverage
track E  由业务 owner 确认影响键和外部副作用

这些 track 默认 R0。任何人提出 R1~R3 动作,都先写成 decision packet;IC 指定唯一 operator 和 verifier。若已有动作执行中,新动作必须说明是等待、互斥还是可以安全并行。

“一个人重启 pool、另一个人切流、第三个人 terminate session”无法从结果中辨别哪个 动作有效,还可能在连接排空过程中制造未知事务。

使用 read-back 和 closed loop

IC:

Authorize action A on cluster pg-x only, node pg-x-2,
expected B within 30 seconds, stop on C, rollback D.

operator 复述 target 和条件,执行后报告:

started / completed UTC
exact command or API request hash
exit status
expected signal B actual value
stop condition C true/false
evidence id

verifier 从独立观察面确认。只有 IC 宣布状态转移后,时间线才从 authorized 变成 completed/verified。这样可以避免“某人说 done”被误读为业务已恢复。

交接要传递未决风险

轮班摘要不应只列已做事项:

current severity / impact / route
authoritative writer and timeline
active fences, pauses, silences and temporary config
last verified data/recovery boundary
actions completed and their results
hypotheses rejected / still open
next action, owner, approval and stop line
irreversible boundaries already crossed
next stakeholder update

接班 operator 对关键身份做一次 read-back。长事件要安排休息和轮换;疲劳是会改变系统 风险的运行条件,不是个人意志问题。

31.5.3 何时必须请求业务、存储、安全或厂商协助

技术 owner 不能替代语义 owner

需要谁 强制触发条件 需要对方决定什么
业务/数据 owner 错误值、target-only 写、退款/通知等副作用 权威状态、补偿规则、可接受降级
应用 owner 重试、连接池、幂等键、deploy/job 相关 围栏范围、回放安全、客户端验收
存储/基础设施 media error、snapshot、空间、fsync/latency 异常 一致快照、fencing、扩容与设备替换
网络/DCS owner leader lock、分区、VIP/route 不一致 多数派、网络围栏和路径恢复
安全/法务/隐私 凭据泄漏、恶意变更、受监管数据、证据可能用于调查 保全、通知、访问范围和 chain of custody
扩展/厂商 非核心插件、内核崩溃、未文档格式或支持合同相关 已知缺陷、受支持恢复路径、补丁

数据库团队可以证明“这 77 行在恢复点之后被合法修改”,却不能决定哪一笔退款该撤销; 存储团队可以生成 crash-consistent snapshot,却不能宣称业务事务完整;厂商可以建议 修复步骤,最终生产授权仍属于本组织。

外部求助包要小而完整

发送前先写一个明确问题:

We need to determine whether PG18.6 can safely start this copied
data directory after error X, without modifying the preserved source.

随包提供:

  • incident ID、版本、OS/架构、扩展与精确错误;
  • system identifier、timeline/LSN 和简化拓扑;
  • 已执行动作、结果和 stop line;
  • 最小可复现样本或隔离工作副本;
  • 相关日志的精确时间窗、散列与脱敏说明;
  • 业务影响和所需答复时限。

不要默认发送整个 postgresql.confpg_hba.conf、Patroni YAML、完整日志、core dump 或业务表;它们可能含凭据、连接串、SQL、参数值和个人数据。先按合同、法律和最小必要 原则确认接收渠道与访问权限。

提前定义“必须停手”

以下情况应停止本地修复尝试,转为保存、隔离和专家协作:

  • 无法确认目标 data directory 的 system identifier 或 timeline;
  • 两个 writer 都有独占提交;
  • pg_resetwal、手工 page copy、catalog hack 等成为唯一候选;
  • pg_rewind 已失败,target 状态未知;
  • 存储错误仍增长,工作副本来源不可靠;
  • 扩展数据格式或加密密钥不受当前团队理解;
  • 任何动作可能影响法定通知或证据可采性。

及时升级不是“把事故甩出去”。IC 仍要跟踪请求 ID、对方假设、建议适用版本、执行 授权和验证结果,并把外部建议纳入同一决策日志。


上一节:决策、沟通与变更纪律 · 返回本章目录 · 下一节:实战:盲抽症状的桌面演练 · 查看全书目录 · 查看索引中心

31.6 实战:盲抽症状的桌面演练

本节不对数据库制造故障。它先从 Pigsty FULL/L3 沙箱采集一份最小只读上下文,再从 八个场景中盲抽两个,把本章协议压缩进前十五分钟。这里有两个容易混淆的 “level”:

Pigsty FULL/L3   监控接入层级:PG + node + pool + HA + logs
experiment L0   风险层级:read-only,在线 mutation 为零

参考 run 的响应由程序生成,用来证明合同和 validator 自洽;真正的人员训练必须由主持人 只发盲包、按请求提供证据卡并独立评分。

31.6.1 随机症状、误导性首发告警与缺失信息

先读合同

完整边界见 lab-contract.md,环境与验收条件见 requirements.json,响应协议见 incident-contract.json,拓扑与路由见 topology.mmd,参与者填写 response-template.json

先做纯静态检查:

static/labs/ch31/task.sh lint

完整参考 run 需要连接已经确认的四节点开发沙箱,但在线阶段只有只读 SQL、Patroni REST 和 pgBackRest info

export PG36_EVIDENCE_DIR="$(
  mktemp -d "${TMPDIR:-/tmp}/pg36-ch31.XXXXXX"
)"
export PG36_CH31_SEED=pg36-ch31-reference-v1

static/labs/ch31/task.sh all

也可以分步执行:

static/labs/ch31/task.sh capture
static/labs/ch31/task.sh exercise
static/labs/ch31/task.sh verify
static/labs/ch31/task.sh review

capture 显式使用 BEGIN READ ONLY、短 statement/lock timeout,只保存聚合会话、复制、 slot、archive、control identity 和缩减后的 HA/backup 上下文。它不读取业务行、不保存 query text 或原始日志。exercise 完全离线,不 pause Patroni、不 failover、不重启、不 terminate connection、不改路由、不恢复 backup。

八个场景让首发症状“不够用”

场景 首发误导 正确主路线
订单数下降、复制延迟为零 把同步副本误当历史副本 PITR
数据库健康、退款突然增长 把技术健康误当语义正确 PITR
写端点 503、primary up 把接入故障误当 primary 故障 HA
DCS timeout、节点自称 primary 相信单节点自报角色 HA
connection timeout、CPU 43% 只看 CPU,忽略连接槽和 pool OVERLOAD
满盘且 replica lag 把 lag 当根因,想手删 WAL OVERLOAD
checksum mismatch、lag 为零 把同步状态当完整性证明 INTEGRITY
seq/index 结果不同 REFRESH VERSION 当重建 INTEGRITY

每个 blind packet 只包含:

  • initial signal 与已报告用户影响;
  • 可以请求的证据卡 ID、问题、层次和时间成本;
  • 十五分钟 deadline 与作答规则。

它不会包含 hidden truth、严重度答案、route、required card、safe/dangerous action 或 证据 observation。facilitator-pack.json 才保存这些内容。场景源文件本身是公开教材, 所以这种隔离是教学流程而非密码学保密;正式考核应由主持人控制文件访问或使用私有 派生场景。

“没有证据”也要进入记录

参与者请求一张暂时不可得的证据卡时,主持人应回答:

unavailable / permission denied / collector down / retention expired

而不是替换成“正常”。参与者要决定是换独立观察面、升级权限 owner、缩小动作,还是 因为关键事实缺失而保持 stop line。事故能力的一部分就是在证据不完整时拒绝不安全的 确定性。

31.6.2 分别按单人和团队模式完成前十五分钟

主持方式

  1. 主持人运行参考工具并单独保管 evidence directory;
  2. 只把一个 blind packet 与空白 response template 发给参与者;
  3. 参与者先声明五轴影响和当前 severity;
  4. 参与者按 ID 请求证据卡,主持人从 facilitator pack 逐张返回 observation;
  5. 每个有副作用的建议都要求 risk、owner、expected、stop、rollback;
  6. 第 15 分钟停止,参与者发布状态更新并完成 handoff;
  7. 最后才揭示 hidden truth、required cards、safe/dangerous actions。

参考 run 固定 seed 抽到:

solo  integrity-collation-index  -> INTEGRITY
team  data-accidental-delete     -> PITR

随机 seed 可改变题目,但 runner 会保证两题属于不同路线。不要重复抽到熟题就假装完成 盲测;应记录 seed、场景使用历史和参与者是否见过答案。

solo 与 team 使用同一张答卷

solo 模式四个 role 都由 solo-oncall 承担,但按时间串行切换;team 模式要求 IC、 operator、scribe、business liaison 是四个不同 actor。两者都必须做到:

required evidence complete minute < route decision minute <= 15
all actions belong to declared safe action set
dangerous_actions_executed = []
decision log starts at minute 0 and ends at minute 15
at least one stakeholder update before or at minute 15
production_authorized = false

团队可以让多个 R0 track 并行,但仍只有一个 operator token;单人不能用“我自己复核过” 冒充独立 R2/R3 review。

一份可复用评分表

项目 分值 失分例
五轴影响与 severity 15 把 unknown 写成无影响
现场和恢复能力保护 20 未围住 writer;建议删除 WAL
跨层证据选择 20 只看一张 dashboard
技术 route 与目标 15 severity 直接决定 failover
决策日志与 stop line 15 动作无预期、回退和结果
角色、升级与沟通 10 多人同时改;不报 unknown
机密与生产边界 5 粘贴凭据;宣称已获生产授权

发生以下任一项应直接判定需要重练,不用总分掩盖:

  • 在证明 writer 唯一前 promote;
  • 删除现役 pg_wal
  • 在原始证据上执行覆盖式修复;
  • 未经业务 owner 直接覆盖事后合法写入;
  • 把 facilitator 未提供的信息自行当成事实。

程序生成的 responses.json 只是一份 schema-complete reference,不是答题者成绩。真人 评分要保存自己的 response、主持人发卡时间和讨论录音/记录权限,并由未参与执行的人 复核。

31.6.3 在 Pigsty L3 输出时间线、决策日志、证据包与路由选择

正式参考现场

正式 run 在 pg-test 捕获:

PostgreSQL version              18.6
system identifier              bound to pgBackRest stanza identity
timeline                       11
Patroni                         1 running primary + 2 running replicas
physical replication streams   2, max sent/replay gap 0 bytes
pgBackRest status / backups     0 / 6
SQL transaction                 READ ONLY
raw query / raw log             absent / absent
online mutation                 none

现场的 pg_stat_archiver.failed_count 为 21,但 last failure 在 2026-07-29T18:57:58Z,last archived success 在 2026-07-30T01:36:16Z。这正说明累计失败数不能直接解释成“当前归档故障”。本章不因 看到 21 就 reset 统计或修改 archive;它保留 reset 与时间,交给真实趋势和 backup 证据解释。

“repo 中有 6 份 backup”也不等于恢复通过。本次没有执行 restore,公开结论只写 pgbackrest_status=0 和 backup count;第 21、32 章的恢复证据才回答可恢复性。

私有证据与公开摘要分层

证据目录包含:

preflight-evidence.json
blind-packets.json
facilitator-pack.json
responses.json
exercise-evidence.json
negative-report.json
validation-report.json
public-summary.json
review.txt

目录权限为 0700、文件为 0600。review 拒绝 credential URI、SCRAM verifier、 private key、clear password、raw SQL/log field,并检查 preflight、盲包、答案、 exercise 与 public summary 属于同一 run。

公开参考摘要见 incident-run.json。它只保留环境轮廓、路线、安全 边界与验收计数,不公开 facilitator 答案和 live 细节。

反例验证防止“格式漂亮的伪响应”

正式 validator 要求:

31 declared counterexamples rejected
18 live evidence mutants rejected
12 source files hash-bound

它会拒绝:

  • severity 被允许直接选择 route;
  • blind packet 泄露 hidden truth 或 expected route;
  • required evidence 未齐就决策;
  • solo/team role 语义不成立;
  • action 属于 dangerous set 或晚于 minute 15;
  • decision log 缺 expected/stop/rollback;
  • live capture 不是 READ ONLY、cluster/timeline/topology 不匹配;
  • backup system identifier 不同;
  • source/upstream/evidence hash 被替换;
  • 任何 production gate 被打开。

对同一份私有证据可以重复:

static/labs/ch31/task.sh verify
static/labs/ch31/task.sh review

但修改实验源文件、盲包或 response 后,不能继续复用旧 exercise hash;应开启新 run, 保留旧包作为历史。

本章交付物不是根因报告

合格的第 31 章 handoff 应包括:

severity and five-axis basis
current user/data/recovery impact
authoritative or still-unknown writer/timeline
active fences and automation state
evidence manifest and decision timeline
chosen route and alternatives rejected
first safe action / stop line / owner
next update and escalation requests

参考 run 最终结论是:

read-only-context-and-tabletop-protocol-demonstrated
human_competency_claimed = false
production_ch31_gate = pending

下一章从第一条路线开始:面对已经提交的误删误改,怎样选准恢复目标、在隔离实例完成 PITR,并把历史正确状态与事后合法写入重新合并。


上一节:单人值守与团队响应 · 返回本章目录 · 下一章:PITR 与误操作恢复——妙手回春 · 查看全书目录 · 查看索引中心