跳至内容
32.4 执行恢复并观察进度

32.4 执行恢复并观察进度

一次 PITR 至少有四个不同的“完成”:

restore copy complete
  -> PostgreSQL first accepts a connection
      -> configured recovery target is reached
          -> candidate data and business invariants pass

pig pitr --no-restart 成功只证明 pgBackRest restore 阶段完成;pg_ctl -w start 成功 可能只代表 hot standby 已可读;pg_is_in_recovery() = false 证明 recovery 已结束, 仍不证明目标业务状态正确。工具状态必须映射到这条状态机。

32.4.1 从已验证备份克隆恢复目标

冻结一份可重放参数包

执行前把变量写入私有 evidence,而不是临场从 shell history 猜:

stanza
repository number
backup label
source system-lineage relation
backup stop LSN/time/timeline
target type and exact normalized value
inclusive/exclusive
target timeline
target action
candidate root
PostgreSQL binary/version
recovery-critical settings
source file hashes
operator, reviewer and authorization

秘密只保存引用或受控位置,不复制进 JSON。参数包要能回答“同一份证据能否重放同一个 候选”,但不能变成凭据泄漏包。

先验证 repository 和 target 可达性

只读预检:

pgbackrest --stanza=pg-test --repo=1 --output=json info
pgbackrest --stanza=pg-test check

检查:

  • stanza status.code = 0
  • exact backup label 存在且 error=false
  • backup type/dependency 与预期一致;
  • backup end 在 target 之前;
  • archive 与 timeline history 覆盖 target;
  • restore host 有足够空间、正确版本和 extension library;
  • candidate root 不存在,端口与 socket path 未占用。

pgbackrest check 成功仍不替代 restore。它验证 repository/stanza 的一组条件,不会运行 本章业务探针。

为 side restore 建一次性目录

本章 runner 只接受形如:

/data/pg36-ch32-restore/run_YYYYMMDDTHHMMSSZ_random/inclusive
/data/pg36-ch32-restore/run_YYYYMMDDTHHMMSSZ_random/exclusive

的 exact path。目录必须:

owner       postgres:postgres
mode        0700
symlink     false
preexisting false
managed PGDATA relation different

Pig 要求 custom -D 预先存在、归 DBSU 所有;它不会代为创建,因为在 destructive classification、owner 检查和 plan 输出之前必须先知道具体目标路径。

先 plan,后 execute

inclusive 候选:

sudo -iu postgres pig pitr \
  -s pg-test -r 1 \
  -b "$BACKUP_LABEL" \
  --xid "$DAMAGE_XID" \
  --target-action=promote \
  --target-timeline=current \
  -D "$INCLUSIVE_ROOT/data" \
  --no-restart \
  --plan \
  -o json \
  -- \
  --archive-mode=off \
  --spool-path="$INCLUSIVE_ROOT/spool" \
  --log-path="$INCLUSIVE_ROOT/log"

审阅 plan 后,在受 guard 约束的自动化中把 --plan 换成 --yes。exclusive 候选只增加:

--exclusive

不要同时改变 backup、timeline、target action 或验证探针,否则两个候选无法归因比较。

Pig 的 first-class 参数负责 target、backup、timeline、data directory 与生命周期; -- 后的原生 pgBackRest 参数不能绕过这些边界。当前 CLI 在 structured output 下要求 显式 --yes 才执行,--plan 是无执行预览路径 (pig pitr Safety Mechanisms)。

读懂 restore 结果

本章正式 run 的 Pig 结果包括:

boundary/effective data dir  exact side path
side_restore                 true
managed data dir             /pg/data
patroni_stopped              false
postgres_restarted           false
backup_set                   exact fresh full
target_type                  xid
target_value                 source-audited XID
exclusive                    true or false
target_action                promote
target_timeline              current

patroni_active=true 在这里是好事:它描述 restore host 上原有 live replica 的 Patroni 没有被 side restore 停掉;隔离 candidate 尚未启动。不能把它误读成“候选已由 Patroni 管理”。

文件恢复失败时

若 pgBackRest restore 失败:

  1. 保留 plan、stdout/stderr、candidate marker 与 repository snapshot;
  2. 不启动部分恢复目录;
  3. 判断是 backup、WAL、key、权限、空间、tablespace 还是版本问题;
  4. 停止任何 exact candidate postmaster;
  5. 只删除当前 marker 所有的 candidate;
  6. 修复原因后开启新 run,不覆盖旧 evidence。

managed restore 失败时,Patroni 可能已停止且 PGDATA 可能只恢复了一部分;不能因失败而 直接重启 Patroni。side restore 则不应改变 managed 生命周期,这正是事故调查优先使用 它的原因。

32.4.2 监控 WAL 重放、目标达成与启动状态

手工启动时覆盖危险继承配置

正式 runner 使用 PostgreSQL 18 的 pg_ctl 启动 custom PGDATA,并通过 -o 传入:

config_file=<candidate>/data/postgresql.conf
hba_file=<candidate>/pg_hba.restore.conf
listen_addresses=''
port=55433
unix_socket_directories=<candidate>/socket
unix_socket_permissions=0700
ssl=off
archive_mode=off
primary_conninfo=''
primary_slot_name=''
shared_preload_libraries=''
logging_collector=off
cluster_name=pg36-ch32-inclusive|exclusive

以及 source 的五项 recovery-critical maxima。完整、安全转义与路径验证见 exercise.py,不要从上面的概念清单拼接未审阅 shell。

第一条连接可能发生在 recovery 中

hot_standby=on,PostgreSQL 达到一致状态后可以开放只读查询,而后继续重放 WAL。 因此:

pg_ctl -w start

-w 只等待 server ready,不保证 target_action=promote 已完成。第 21 章正式实验 已经实际观察到:

first connection  recovery=true, transaction_read_only=true
later             recovery=false, writable=true

本章 runner 继续轮询:

SELECT json_build_object(
    'in_recovery', pg_is_in_recovery(),
    'transaction_read_only',
        current_setting('transaction_read_only')::boolean,
    'last_replay_lsn', pg_last_wal_replay_lsn(),
    'replay_paused', pg_is_wal_replay_paused()
);

只有 pg_is_in_recovery() = false 才表示 promote action 已完成。随后仍先运行只读业务 探针;“可写”只是候选能力,不是允许应用写入。

分解恢复时延

用一个总秒数会掩盖瓶颈。本章分开测:

target_identification_ms
plan_ms
restore_copy_ms
start_to_first_connection_ms
start_to_promoted_ms
candidate_validation_ms
reconciliation_ms

关系:

$$ T_{\text{technical}} = T_{\text{identify}}

  • T_{\text{plan}}
  • T_{\text{copy}}
  • T_{\text{replay}}
  • T_{\text{validate}} $$

真实 RTO 还要加:

$$ T_{\text{service}} = T_{\text{technical}}

  • T_{\text{reconcile}}
  • T_{\text{cutover}}
  • T_{\text{application validate}}
  • T_{\text{approval}} $$

本章没有执行后三项,所以不会从 2.8 秒 restore 推导“生产 RTO 3 秒”。

监控哪些进度

阶段观察正常推进阻断
文件 restorepgBackRest log/resultfiles restored, exit 0missing backup/key/space
启动postmaster log、PID、socketstartup then consistent stateconfig/library/tablespace error
archive replayPostgreSQL log、replay LSNrequested WAL advancesrepeated missing/corrupt WAL
targetlog + target-specific datastop before/after expected committarget not reached
actionrecovery functionspaused/promoted/shutdown as declaredunexpected action
validationSQL manifestexpected safe/bad/post setsany invariant mismatch

没有通用的“百分比”能准确表示 archive recovery,因为最终 target 与可用 WAL、restore latency、replay workload 都会影响剩余工作。比虚构 73% 更有用的是保存已请求 WAL、 last replay LSN、目标和时间趋势。

target 未达到就是失败

PostgreSQL 明确规定:配置了 recovery target,但 archive recovery 在到达它之前结束, server 会以 fatal error 停止 (recovery_target_action)。

常见原因:

  • 所选 backup 本来就在 target 之后;
  • 中间 WAL 缺失或不可读;
  • timeline 选错;
  • target time/XID/LSN 根本不在该历史;
  • repository credential 或网络中断;
  • target 格式/时区错误。

禁止把 last replay LSN 当成“近似成功点”继续交付。应回到血缘与 target 证据。

32.4.3 用原生文件、日志和 SQL 复核工具状态

三个观察面互相校验

文件/配置面

PG_VERSION
backup_label or backup metadata
postgresql.conf / include chain
postgresql.auto.conf
recovery.signal
standby.signal
tablespace links
postmaster.pid / postmaster.opts

现代 PostgreSQL 通过 recovery.signal 进入 targeted recovery;若同时有 standby.signal,standby mode 优先。pgBackRest 会把 recovery 设置写入有效配置, 操作者应读 effective result,不能只看手写模板。

日志面

关注事件顺序,而不是只搜 ready

selected timeline / restore command
redo starts at ...
restored log file ...
consistent recovery state reached
recovery stopping before|after target transaction/time/LSN
redo done
new timeline selected/created
database system is ready to accept connections
fatal target not reached or WAL unavailable

日志时间必须与第 32.2 节的时区证据绑定。敏感业务值、凭据和原始查询不应未经筛选进入 公开 evidence。

SQL/控制面

SELECT
    pg_is_in_recovery(),
    pg_is_wal_replay_paused(),
    pg_last_wal_receive_lsn(),
    pg_last_wal_replay_lsn(),
    pg_last_xact_replay_timestamp();

SELECT * FROM pg_control_checkpoint();
SELECT * FROM pg_control_recovery();
SELECT * FROM pg_control_system();

在 promote 后,一些 recovery 函数会返回 NULL 或历史最后值,这是状态语义,不是自动 错误。原始 system identifier 应在私有证据中受保护;公开摘要通常只需要 “matches source lineage=true”。

工具结论必须回到原生事实

工具说法原生复核
side restoreeffective PGDATA 与 managed PGDATA 不同;Patroni 未停
archive offSHOW archive_mode
target promotepg_is_in_recovery() = false 与日志
exact timelinebackup/history/config 与 control data
no TCPSHOW listen_addresses + OS socket table
candidate stoppedexact postmaster.pid/socket absent
backup retainedrestore 后再次读取 repository catalog

平台包装减少操作错误,但不会改变 PostgreSQL 的恢复语义。证据同时保留 Pig structured result 与原生复核,才能在 CLI 版本变化或输出解释争议时继续审计。

pg_waldump 的位置

pg_waldump 可在高级调查中识别 WAL record、事务 commit/abort 与 relation 变化,但:

  • 需要匹配 PostgreSQL major;
  • 输出是低层内部格式,不是业务审计;
  • 应分析受保护副本或 archive 文件;
  • 不应在运行中的 active pg_wal 上做会干扰现场的操作;
  • 结果仍需与日志、catalog 和业务 identity 关联。

本章正式 run 不靠 pg_waldump 猜 XID,而是从同一错误事务写入的 source audit 取目标, 再由两个候选证明包含关系。

候选状态表

恢复完成后先填:

candidate:
  backup_label: exact
  target:
    type: xid
    value_source: source-audit
    inclusive: true | false
    timeline: current
    action: promote
  runtime:
    in_recovery: false
    archive_mode: off
    tcp_listener: false
    private_socket: true
    patroni_managed: false
  data:
    safe_present: true | false
    damage_present: true | false
    post_target_present: true | false
  decision: accepted | rejected | blocked

下一节才把 accepted candidate 与当前 source、good-after manifest 和外部系统放在一起, 决定提取、修补或切换。


上一节:隔离恢复策略 · 返回本章目录 · 下一节:数据验证与安全回切 · 查看全书目录 · 查看索引中心

最后更新于