32.4 执行恢复并观察进度
一次 PITR 至少有四个不同的“完成”:
restore copy complete
-> PostgreSQL first accepts a connection
-> configured recovery target is reached
-> candidate data and business invariants passpig 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 differentPig 要求 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 currentpatroni_active=true 在这里是好事:它描述 restore host 上原有 live replica 的 Patroni
没有被 side restore 停掉;隔离 candidate 尚未启动。不能把它误读成“候选已由 Patroni
管理”。
文件恢复失败时
若 pgBackRest restore 失败:
- 保留 plan、stdout/stderr、candidate marker 与 repository snapshot;
- 不启动部分恢复目录;
- 判断是 backup、WAL、key、权限、空间、tablespace 还是版本问题;
- 停止任何 exact candidate postmaster;
- 只删除当前 marker 所有的 candidate;
- 修复原因后开启新 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 秒”。
监控哪些进度
| 阶段 | 观察 | 正常推进 | 阻断 |
|---|---|---|---|
| 文件 restore | pgBackRest log/result | files restored, exit 0 | missing backup/key/space |
| 启动 | postmaster log、PID、socket | startup then consistent state | config/library/tablespace error |
| archive replay | PostgreSQL log、replay LSN | requested WAL advances | repeated missing/corrupt WAL |
| target | log + target-specific data | stop before/after expected commit | target not reached |
| action | recovery functions | paused/promoted/shutdown as declared | unexpected action |
| validation | SQL manifest | expected safe/bad/post sets | any 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 restore | effective PGDATA 与 managed PGDATA 不同;Patroni 未停 |
| archive off | SHOW archive_mode |
| target promote | pg_is_in_recovery() = false 与日志 |
| exact timeline | backup/history/config 与 control data |
| no TCP | SHOW listen_addresses + OS socket table |
| candidate stopped | exact postmaster.pid/socket absent |
| backup retained | restore 后再次读取 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 和外部系统放在一起, 决定提取、修补或切换。
上一节:隔离恢复策略 · 返回本章目录 · 下一节:数据验证与安全回切 · 查看全书目录 · 查看索引中心