恢复配置(restore_command / recovery_target)
"恢复配置"指的是:当我们已经准备好一份基础备份、并且归档里有连续的 WAL 时,如何告诉 PostgreSQL "把 WAL 从哪里取来、重放到哪里停、停下来之后做什么"。这三件事分别由 restore_command、recovery_target_* 和 recovery_target_action 决定。
从 PostgreSQL 12 开始,这些参数不再写在独立的 recovery.conf 里,而是写在常规配置文件中(postgresql.conf 或 postgresql.auto.conf),并通过在数据目录中放置一个信号文件来触发恢复模式。这是版本演进中变化最大的一处,也是很多老教程给出错误做法的根源——本页会先把这一点讲清楚,再逐项展开其他参数。
需要强调的一点是:恢复配置是"事后"的、临时的配置。它只在一次恢复过程中有意义,恢复完成后通常会被清理掉(例如删除信号文件)。因此不要把它当成长期配置写进 postgresql.conf 的主配置区,更合适的做法是在恢复时临时追加、恢复完成后移除,避免下次意外启动又进入恢复模式。
前置知识
基础概念
恢复配置的承载方式变迁
| 版本 | 恢复参数写在哪 | 触发方式 |
|---|---|---|
| PostgreSQL 11 及更早 | 数据目录下的 recovery.conf | 该文件存在即进入恢复;处理后被改名为 recovery.done |
| PostgreSQL 12 及以后 | postgresql.conf / postgresql.auto.conf | 数据目录下存在 recovery.signal 或 standby.signal |
关键结论:recovery.conf 在 PostgreSQL 12 已被彻底移除。官方明确说明:如果数据目录中存在 recovery.conf,服务端将拒绝启动。因此按老教程创建 recovery.conf 不仅不会生效,还会直接导致实例起不来。正确做法是把参数写进配置文件,再 touch recovery.signal。
同一份官方说明里还提到两个相关的移除项:standby_mode 参数被移除,改由 standby.signal 文件表达;更早的 trigger_file / promote_trigger_file(PostgreSQL 15 及更早版本存在)也被移除,提升改为使用 pg_ctl promote 或 pg_promote()。
两个信号文件
| 信号文件 | 语义 | 恢复完成后 |
|---|---|---|
recovery.signal | 这次是"有终点的恢复"(PITR) | 到达目标后按 recovery_target_action 处理 |
standby.signal | 这次是"持续恢复"(备库),没有终点 | 永不主动停止,持续等待新 WAL |
两者互斥,同时存在会导致启动失败。区别只在"重放是否会停":
- 有
recovery.signal:重放到recovery_target命中的位置后停止,或重放到 WAL 末尾后停止。 - 有
standby.signal:一直重放,等待restore_command或primary_conninfo提供更多 WAL。
因此,如果只是想搭一个备库,用 standby.signal;如果要把库恢复到过去某个时间点,用 recovery.signal。
恢复相关参数总览
| 参数 | 作用 | 生效方式 |
|---|---|---|
restore_command | 从归档取 WAL 的命令 | reload / 启动时 |
archive_cleanup_command | 清理不再需要的归档文件 | reload / 启动时 |
recovery_end_command | 恢复结束时执行一次的命令 | reload / 启动时 |
recovery_target | 特殊值 immediate,到达一致点即停 | 启动时 |
recovery_target_name | 按命名还原点停止 | 启动时 |
recovery_target_time | 按时间戳停止 | 启动时 |
recovery_target_xid | 按事务 ID 停止 | 启动时 |
recovery_target_lsn | 按 WAL 位置停止 | 启动时 |
recovery_target_inclusive | 是否包含恰好命中的记录 | 启动时 |
recovery_target_timeline | 跟随哪条时间线 | 启动时 |
recovery_target_action | 到达目标后的动作 | 启动时 |
recovery_min_apply_delay | 延迟重放(用于备库) | reload / 启动时 |
注意"启动时生效"这一类:它们只在恢复启动阶段读取,恢复过程中修改没有意义。
restore_command
restore_command 是一个 shell 命令,用来从归档位置取回一个 WAL 文件。它支持与 archive_command 相同的占位符:
| 占位符 | 含义 |
|---|---|
%f | 要恢复的 WAL 段文件名 |
%p | 恢复目标的完整路径(注意与 archive_command 的 %p 语义不同) |
%% | 一个字面量百分号 |
这条命令的失败语义非常重要:找不到文件时应当立即返回非 0,不要等待、不要重试、不要阻塞。服务端会在需要时再次调用它。如果命令内部做了长时间等待(例如等网络挂载点恢复),恢复进程会被白白拖住。
recovery_target 系列
recovery_target 是一个"特殊值"参数,只有一个合法取值 immediate;它的同族参数才是真正的目标指定方式。这组参数只能设置一个(immediate 与其他目标类型互斥),设置多个非 immediate 的目标,服务端会报错。
| 参数 | 停止条件 | 典型用途 |
|---|---|---|
recovery_target | 到达一致点立即停止 | 只想要"能启动的最小状态" |
recovery_target_name | 命中指定还原点 | 事前打过还原点的场景 |
recovery_target_time | 重放到指定时间戳 | 已知误操作时间的场景 |
recovery_target_xid | 重放到指定事务 | 已知事务 ID 的场景 |
recovery_target_lsn | 重放到指定 WAL 位置 | 最精确的场景 |
recovery_target_action
这个参数决定"到达目标之后做什么",取值三个:
| 取值 | 行为 | 说明 |
|---|---|---|
pause | 暂停在目标点,等待人工决定 | 默认值,最安全 |
promote | 立即提升为可写主库 | 自动化恢复常用 |
shutdown | 停止服务 | 用于"先恢复出数据、暂不对外提供"的场景 |
特别要注意默认值是 pause:如果没设置 recovery_target_action,恢复到达目标后会暂停,实例可以接受只读查询,但不会自动变成可写状态。这与很多人"恢复完自动变主库"的预期不同。
recovery_min_apply_delay
这个参数让备库在重放 WAL 时人为延迟一段时间再应用。它不是给 PITR 用的,而是给备库用的(需要配合 standby.signal)。
它的典型价值是:当主库发生误操作时,备库因为延迟,还没有执行这条误操作的 WAL,运维可以在延迟窗口内把备库提升,从而"顺便"完成一次快速恢复。
代价是:
- 备库的数据天然落后一段时间(这是设计意图);
- 只有"提交时间戳"到达之后才会应用(延迟的度量基于 WAL 记录里的时间戳);
- 延迟会传播到下游备库(级联复制时);
- 大幅延迟的备库在切换时会损失对应时间的数据。
语法与用法
最简恢复配置
# postgresql.conf 追加(或写入 postgresql.auto.conf)
# 从归档取 WAL
restore_command = 'cp /mnt/wal_archive/%f %p'
# 恢复到某个时间点
recovery_target_time = '2026-09-20 10:30:00+08'
# 到达目标后提升为可写主库
recovery_target_action = 'promote'# 在数据目录中放置恢复信号文件
touch $PGDATA/recovery.signal恢复过程的日志
LOG: starting point-in-time recovery to 2026-09-20 10:30:00+08
LOG: restored log file "0000000100000000000000B2" from archive
LOG: redo starts at 0/16000028
LOG: consistent recovery state reached at 0/16B3748
LOG: recovery stopping before commit of transaction 743, time 2026-09-20 10:31:12.339251+08
LOG: redo done at 0/16C4D00
LOG: selected new timeline ID: 3
LOG: archive recovery complete检查当前是否处于恢复状态
-- 返回 true 表示当前仍在恢复(或处于备库模式)
SELECT pg_is_in_recovery(); pg_is_in_recovery
-------------------
f
(1 row)-- 恢复进度相关(在恢复中才有意义)
SELECT pg_last_wal_receive_lsn() AS received,
pg_last_wal_replay_lsn() AS replayed,
pg_last_xact_replay_timestamp() AS last_replay_time; received | replayed | last_replay_time
-------------+------------+---------------------------
0/16C4D00 | 0/16C4D00 | 2026-09-20 10:31:12.339+08
(1 row)手工提升
-- 如果 recovery_target_action = 'pause',可手工提升
SELECT pg_promote(); pg_promote
------------
t
(1 row)-- 带等待与超时的写法(wait 为 true 时等待完成,timeout 单位为秒)
SELECT pg_promote(true, 60);详细说明
为什么弃用 recovery.conf
recovery.conf 的设计有一个明显的缺陷:它是"数据目录下的一个文件",而"文件是否存在"同时承担了"参数容器"和"是否恢复"两个职责。这带来几个问题:
- 配置文件分裂。一个实例的配置分散在
postgresql.conf、postgresql.auto.conf、recovery.conf三处,ALTER SYSTEM无法统管,排查配置来源困难。 - 状态难表达。恢复完成后文件被改名,但这个"改名的动作"本身不是事务性的,异常中断时状态可能不清晰。
- 与备库概念纠缠。备库本质上就是"永不停止的恢复",但
recovery.conf的名字暗示它是一个"一次性恢复"。
PostgreSQL 12 的改法是把"参数"归入常规配置文件(从而被 ALTER SYSTEM、统一配置管理覆盖),把"是否恢复"单独交给信号文件表达。这个划分更清晰,也让恢复参数可以被工具(如 pg_basebackup 的 -R)以标准方式写入。
信号文件的创建位置与内容
信号文件位于数据目录根部,内容是空的(只起标记作用):
# 数据目录中应该看到的样子
ls -la $PGDATA/*.signal-rw-r--r-- 1 postgres postgres 0 Sep 28 10:20 /var/lib/postgresql/18/main/recovery.signal注意几点:
- 文件必须是空文件,内容会被忽略;
- 权限需要让 PostgreSQL 运行用户可读;
- 恢复完成(提升)后,服务端会自动删除
recovery.signal,避免下次启动又进入恢复; standby.signal在提升时也会被自动删除。
restore_command 的占位符
restore_command 的占位符与 archive_command 有一处关键差异:%p 在恢复时指向恢复目标的完整路径(即要把 WAL 写到哪),而 %f 是要恢复的文件名。因此命令通常是"从归档取到 %p":
# 从本地归档目录取
restore_command = 'cp /mnt/wal_archive/%f %p'
# 以"不存在则失败"的方式取(推荐:找不到就立即返回非 0)
restore_command = 'test ! -f /mnt/wal_archive/%f && exit 1; cp /mnt/wal_archive/%f %p'
# 支持压缩归档(先解压再写入)
restore_command = 'gunzip -c /mnt/wal_archive/%f.gz > %p'
# 从对象存储拉取
restore_command = 'aws s3 cp s3://wal-bucket/%f %p'
# 从远端主机拉取,带超时避免挂起
restore_command = 'timeout 30 scp archive@host:/wal/%f %p'压缩归档是一个很实用的技巧:把 WAL 用 gzip 压缩后归档,恢复时解压,可以显著减少归档存储占用。代价是归档时需要额外一步(例如在归档脚本里压缩),且 restore_command 要对应处理。
停止位置的语义细节
recovery_target_* 决定"在哪里停",但"停"的精确语义还受 recovery_target_inclusive 影响。
| 目标类型 | 默认 inclusive = true 的含义 | inclusive = false 的含义 |
|---|---|---|
recovery_target_time | 重放所有提交时间 ≤ 目标的记录 | 只重放提交时间 < 目标的记录 |
recovery_target_xid | 包含指定事务本身 | 不包含指定事务 |
recovery_target_lsn | 包含到达该 LSN 的记录 | 不包含到达该 LSN 的记录 |
recovery_target_name | 包含还原点记录 | —(对命名还原点通常保持默认) |
还有一个必须知道的特例:recovery_target_xid 按"事务 ID 分配顺序"而非"提交顺序"来停止。事务 ID 只是发起顺序,提交顺序可能不同。因此 recovery_target_xid 在某些情况下可能停止在一个与直觉不一致的位置——它会在"所有 XID 小于目标的记录都已重放"处停止,即使那些事务的提交时间晚于目标。这一点让 recovery_target_xid 的实用性不如 recovery_target_time 和 recovery_target_lsn。
多个目标参数如何处理
如果同时设置了多个目标参数,服务端会报错并拒绝启动。这是有意的设计,避免"以为按时间停、实际按 LSN 停"这类歧义。
但有一个例外组合是可以的:recovery_target(immediate)与 recovery_target_action。因为前者是"不指定具体目标",后者是"到达后做什么",二者不冲突。
recovery_target_action 的三个取值详解
| 取值 | 到达目标后 | 对连接的影响 | 适用场景 |
|---|---|---|---|
pause | 停在目标点 | 可接受只读连接 | 需要人工核对数据后再决定 |
promote | 提升为可写 | 可接受读写连接 | 自动化恢复脚本 |
shutdown | 关闭实例 | 连接被断开 | 只想"恢复出文件",稍后再启动 |
shutdown 的典型用法:需要把恢复出来的数据用文件系统工具检查、或者需要先复制某些文件出来时,让实例保持关闭状态比让它以只读方式开放更省事。
pause 状态下如何继续,有三种方式:
-- 方式一:提升为可写主库(最常用)
SELECT pg_promote();# 方式二:告诉服务端继续重放到下一个目标(需要重新配置目标)
# 实际操作是:停止服务 → 修改 recovery_target_* → 重启
# 方式三:直接提升(等价于 pg_promote,需要服务在 pause 状态)
pg_ctl promote -D $PGDATApg_ctl promote 与 pg_promote() 都能完成提升,区别是前者是外部命令(不需要数据库连接),后者需要能连上实例。
archive_cleanup_command
恢复过程中会不断从归档取 WAL。已经不再需要的归档文件可以由 archive_cleanup_command 清理,官方配套的工具是 pg_archivecleanup:
archive_cleanup_command = 'pg_archivecleanup /mnt/wal_archive %r'%r 是"当前恢复所需的起始 WAL 文件",pg_archivecleanup 会删除归档目录中早于该文件的所有 WAL 段。
这个参数主要用于备库:备库长期运行,归档目录如果无人清理会无限增长。但要注意风险:
- 如果还有其他实例(或其他恢复目标)依赖更早的归档,清理会破坏它们的恢复能力;
- 删除是不可逆的,应确保归档另有异地备份。
因此在"多实例共享归档目录"或"需要保留历史恢复点"的场景下,不要启用自动清理,而是用独立的保留策略脚本统一管理。
recovery_end_command
recovery_end_command 在恢复完成时执行一次,常用于做通知、标记:
recovery_end_command = 'touch /var/lib/postgresql/recovery_finished_$(date +%F_%H%M%S)'它的作用域很窄(只执行一次),且如果它失败会记录警告,但不影响恢复结果。因此适合做"打点",不适合做"必须成功的业务动作"。
recovery_min_apply_delay
这个参数的单位是毫秒(也接受带单位的时间写法)。它让备库延迟应用 WAL:
# 备库延迟 30 分钟应用
recovery_min_apply_delay = '30min'语义细节:
- 延迟针对的是提交时间戳,即"从 WAL 记录里的提交时间算起,至少过 30 分钟才应用";
- 如果主库和备库的系统时钟差异很大,延迟行为会受影响;
- 只有"可重放的提交"(有提交时间戳的记录)会被延迟,其他记录不受影响;
- 延迟会级联:如果备库 B 从备库 A 拉 WAL,A 的延迟会叠加到 B 上。
一个重要的判断:延迟备库不是备份的替代品。它只能对抗"发现得很快"的误操作,一旦误操作在延迟窗口之后才被发现,备库也已经应用了。真正的兜底仍然是"基础备份 + WAL 归档"的 PITR。
从备份目录启动恢复的完整参数组合
把恢复所需的参数集中列出来,形成一个可直接使用的模板:
# --- 恢复参数(仅恢复期间需要)---
# 1. WAL 来源
restore_command = 'cp /mnt/wal_archive/%f %p'
# 2. 恢复目标(四选一;或用 recovery_target = 'immediate')
recovery_target_time = '2026-09-20 10:30:00+08'
# 3. 边界是否包含命中的记录
recovery_target_inclusive = on
# 4. 跟随的时间线(默认 latest)
recovery_target_timeline = 'latest'
# 5. 到达目标后的动作
recovery_target_action = 'promote'
# 6. 归档清理(可选,谨慎使用)
# archive_cleanup_command = 'pg_archivecleanup /mnt/wal_archive %r'
# 7. 恢复结束命令(可选)
# recovery_end_command = 'echo recovery done'# 启动前确认两件事:
# 1) 数据目录里没有 standby.signal(它会让实例变成"永不停止的备库")
# 2) 数据目录里有 recovery.signal
ls -la $PGDATA/*.signal 2>/dev/null
touch $PGDATA/recovery.signal恢复失败时的常见错误
| 日志 / 现象 | 原因 | 处理 |
|---|---|---|
could not find WAL file 反复出现并最终失败 | 归档缺段或 restore_command 路径错 | 检查归档连续性、占位符 |
recovery ended before configured recovery target was reached | 目标超出 WAL 覆盖范围 | 修正目标,或补齐 WAL |
invalid checkpoint record / 启动即失败 | 基础备份不完整或版本不匹配 | 重新获取基础备份 |
recovery_target_time is before the last checkpoint | 目标早于基础备份起点 | 目标不可达,需更早的备份 |
| 一直停在 pause,服务只读 | recovery_target_action 为默认 pause | 设为 promote 或手工 pg_promote() |
| 服务无法启动,提示信号文件冲突 | 同时存在 recovery.signal 与 standby.signal | 只保留一个 |
上表中前两类错误最值得提前理解:"目标超出 WAL 覆盖范围"和"目标早于备份起点"是恢复目标选择的两个硬边界——可达的恢复区间是 [基础备份起点, 已归档 WAL 末尾]。
实战示例
示例一:恢复到时间点(最典型流程)
# 1. 停止服务
sudo systemctl stop postgresql-18
# 2. 保全现场(改名而不是删除)
mv $PGDATA $PGDATA.old_20260928
mkdir -p $PGDATA
chmod 0700 $PGDATA
chown postgres:postgres $PGDATA
# 3. 展开基础备份到数据目录
tar -xzf /backup/base_20260928.tar.gz -C $PGDATA
# 注意:tar 包内的目录结构可能需要调整,确保 PG_VERSION 直接位于 $PGDATA 下
# 4. 写入恢复参数
cat >> $PGDATA/postgresql.auto.conf <<'EOF'
restore_command = 'cp /mnt/wal_archive/%f %p'
recovery_target_time = '2026-09-20 10:30:00+08'
recovery_target_action = 'promote'
EOF
# 5. 创建恢复信号文件
touch $PGDATA/recovery.signal
# 6. 修正属主与权限
chown -R postgres:postgres $PGDATA
chmod 0700 $PGDATA
# 7. 启动并观察日志
sudo systemctl start postgresql-18
sudo tail -f /var/log/postgresql/postgresql-18-main.log-- 8. 验证:确认已提升(返回 f)、确认目标时刻的数据状态
SELECT pg_is_in_recovery();
SELECT count(*) FROM orders WHERE created_at > '2026-09-20 10:30:00+08';示例二:使用命名还原点恢复
-- 事前:在关键操作前打还原点
SELECT pg_create_restore_point('before_price_adjust_20260928'); pg_create_restore_point
--------------------------
0/1B2C340
(1 row)# 恢复时的配置
restore_command = 'cp /mnt/wal_archive/%f %p'
recovery_target_name = 'before_price_adjust_20260928'
recovery_target_action = 'promote'命名还原点的优势是"不依赖时钟、不依赖人回忆时间"——目标的语义由业务动作定义,最不容易出错。
示例三:按 LSN 精确恢复
先从 WAL 里找出目标位置。pg_waldump 可以读 WAL 内容:
pg_waldump -p /mnt/wal_archive 0000000100000000000000B3 | grep -i 'DELETE'rmgr: Heap len (rec/tot): 59/ 59, tx: 743, lsn: 0/16C4D00, prev 0/16C4CC8, desc: DELETE off 5 flags 0x00 KEYS_UPDATED , blkref #0: rel 1663/16384/24576 blk 12注意到错的删除事务的 LSN 是 0/16C4D00,于是把目标设在该位置之前:
restore_command = 'cp /mnt/wal_archive/%f %p'
recovery_target_lsn = '0/16C4D00'
recovery_target_inclusive = off # 不包含该条记录
recovery_target_action = 'promote'这是精度最高的恢复方式:不受时间戳精度、时钟偏移的影响。
示例四:配置延迟备库
# 备库(有 standby.signal)的配置
# 主库信息
primary_conninfo = 'host=10.0.1.10 port=5432 user=replicator application_name=standby1'
# 延迟 30 分钟应用
recovery_min_apply_delay = '30min'-- 在备库上观察延迟(received 应明显领先 replayed)
SELECT pg_last_wal_receive_lsn() AS received,
pg_last_wal_replay_lsn() AS replayed,
pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn()) AS lag_bytes,
now() - pg_last_xact_replay_timestamp() AS lag_time
FROM pg_stat_replication
LIMIT 1; received | replayed | lag_bytes | lag_time
------------+------------+-----------+----------------
0/1B2C340 | 0/1B2B000 | 4792 | 00:29:52.113
(1 row)lag_time 稳定在接近 30 分钟,说明延迟生效。
示例五:从 pause 状态决定继续或放弃
-- 恢复到达目标,处于 pause 状态,先做只读核对
SELECT pg_is_in_recovery(); pg_is_in_recovery
-------------------
t
(1 row)-- 核对数据是否符合预期
SELECT max(id) AS max_id, count(*) AS cnt FROM important_table;
SELECT * FROM important_table ORDER BY id DESC LIMIT 5;-- 确认无误后提升
SELECT pg_promote(); pg_promote
------------
t
(1 row)-- 提升后再确认一次
SELECT pg_is_in_recovery(); pg_is_in_recovery
-------------------
f
(1 row)如果核对后发现恢复目标选得不对(例如停在误操作之后了),正确做法是停止服务、重新配置目标、重新从基础备份开始恢复,而不是试图在 pause 状态下"往回退"——恢复只能单调向前。
注意事项
不要在 postgresql.conf 里长期保留恢复参数。 恢复参数是临时的。如果它们留在那里,下次意外以恢复模式启动时,实例会尝试再次恢复,可能造成混乱。更安全的做法是写入 postgresql.auto.conf(因为 ALTER SYSTEM 生成的配置集中在这里,便于用 ALTER SYSTEM RESET 清理),或者恢复结束后手工移除。
restore_command 找不到文件时立即返回非 0。 这是官方明确要求的语义。如果命令内部 sleep 或重试,会把恢复进度拖成"看起来卡住"。远端拉取一定要加超时。
recovery_target_xid 的停止语义与直觉不同。 它按事务 ID 分配顺序停止,而不是提交顺序。能用时间或 LSN 就别用 XID。
recovery_target_action 默认值是 pause。 忘记设置它会得到"恢复完了但只读"的结果。自动化脚本里务必显式指定 promote。
recovery.signal 与 standby.signal 不能共存。 从基础备份搭备库时如果用错了信号文件(用了 recovery.signal),实例会在重放到 WAL 末尾后停止甚至提升,而不是持续跟随主库。
恢复目标必须落在 [备份起点, 归档末尾] 区间内。 超出区间会导致恢复失败(目标太晚)或立即判定不可达(目标太早)。恢复前先用 restore_command 手工拉一个段验证归档可用性,再全量恢复。
恢复过程中不要并发访问数据目录。 任何外部进程(备份软件、同步工具、手工 cp)都不能在恢复期间读写数据目录。
archive_cleanup_command 会删除文件且不可逆。 在多实例共享归档目录的场景下启用它,风险极高。默认建议不启用,交由独立策略管理归档保留。
recovery_min_apply_delay 会放大切换时的数据损失。 设置了 30 分钟延迟,意味着主库故障切到该备库时,最多可能丢 30 分钟数据。因此延迟备库通常应与"正常同步备库"并存,而不是替代它。
恢复完成后实例处于新时间线,需要立即重建归档与备份链路。 恢复完就不管,会让新实例实际上"无保护"。
不要忘记恢复前的现场保全。 把原数据目录改名保留,是恢复失败时唯一的退路。删除是不可逆的。
最佳实践
把恢复参数写入
postgresql.auto.conf而不是手工编辑postgresql.conf。 前者可以用ALTER SYSTEM RESET ALL或逐项ALTER SYSTEM RESET清理,且不会被配置管理工具覆盖。恢复前先做一次"取 WAL"预检。 手工执行一次
restore_command的等价命令,确认能取到目标区间的第一个和最后一个 WAL 段。这一步能提前发现归档缺段、权限、网络等问题,避免恢复跑到一半才失败。显式设置
recovery_target_action。 自动化恢复脚本必须指定promote;需要人工核对的场景则明确用pause,并在 Runbook 里写清"核对什么、怎么判断、怎么继续"。优先用命名还原点作为恢复目标。
recovery_target_name的语义最稳定,不依赖时钟精度、不依赖事务 ID 分配顺序。把"打还原点"作为高风险操作的固定前置步骤。配置里保留
recovery_target_inclusive的显式取值。 边界是否包含会导致"差一条记录",在审计严格的场景下这是不可接受的模糊。显式写出来,避免依赖默认值。恢复用独立实例或独立端口。 不要在生产实例的原数据目录上直接做恢复,除非已做好现场保全。把恢复放到另一台机器或另一个端口,可以在不影响生产的前提下核对数据,再从恢复实例导出所需数据。
把恢复参数模板化并纳入版本管理。 把参数组合写成模板文件(按目标类型分类),恢复时只需填空。避免在事故中临时手写参数。
在恢复脚本里加入自动校验。 恢复启动后自动等待并检查日志关键字(如
archive recovery complete),并执行几个关键业务查询做数据核对,把结果输出到报告。为
restore_command准备多条回退路径。 例如"先查本地缓存目录,再查对象存储",用 shell 的||串联。这样即使某一路径不可用,恢复也能继续。定期验证"归档目录到恢复目标"的可达区间。 定期列出归档中最早与最新的 WAL 段,确认最远可恢复时间点符合 RPO 承诺。
延迟备库只作为补充手段。 它的价值在于缩短"发现得快"的误操作的恢复时间,不能替代 PITR。部署时同时保留一个无延迟的同步备库用于高可用切换。
恢复完成后立即执行三项收尾。 重做一次基础备份、确认归档链路恢复、验证监控告警恢复正常。缺少任何一项,实例都处于"看起来正常但实际无保护"的状态。
下一节
继续学习:按时间 / LSN / 事务 ID 恢复
参考链接
- PostgreSQL 官方文档:Recovery Configuration
- PostgreSQL 官方文档:Continuous Archiving and Point-in-Time Recovery (PITR)
- PostgreSQL 官方文档:Write Ahead Log — Configuration Parameters
- PostgreSQL 官方文档:Log-Shipping Standby Servers
- PostgreSQL 官方文档:System Administration Functions — Recovery Control Functions
- PostgreSQL 官方文档:pg_archivecleanup
- PostgreSQL 官方文档:pg_waldump
- PostgreSQL 官方文档:pg_ctl
- PostgreSQL 官方文档:pg_basebackup