21.2 物理备份与 WAL 连续性
物理 PITR 可以写成一条依赖式:
R e c o v e r a b l e ( t ) = B a s e B a c k u p ( b ) ∧ C o n t i n u o u s W A L ( b s t a r t , t ) ∧ R e a c h a b l e T i m e l i n e ( t ) ∧ R u n t i m e C o m p a t i b i l i t y
Recoverable(t)=
BaseBackup(b)
\land ContinuousWAL(b_{start},t)
\land ReachableTimeline(t)
\land RuntimeCompatibility
R eco v er ab l e ( t ) = B a se B a c k u p ( b ) ∧ C o n t in u o u s W A L ( b s t a r t , t ) ∧ R e a c hab l e T im e l in e ( t ) ∧ R u n t im e C o m p a t ibi l i t y 其中任何一项缺失,都不是“少恢复一点”,而可能是整条恢复链不可执行。
21.2.1 基础备份、检查点与一致性起点
为什么运行中的文件可以备份
在线复制数据目录时,文件并不处在同一个瞬间:
relation A copied at 10:00:01
relation B page modified at 10:00:02
relation B copied at 10:00:03
catalog copied at 10:00:04
单看文件副本,它可能不一致。PostgreSQL 的保证来自:
备份在一个已知 checkpoint 边界开始;
保存恢复所需的 backup metadata;
保存备份期间及之后所需的 WAL;
恢复时从 redo 起点重放到一致状态。
所以物理备份不是“恰好一致的一篮子文件”,而是:
recoverable file image + exact WAL obligations
checkpoint 在做什么
checkpoint 把一个恢复起点写入控制信息,并推动 dirty buffer 写盘。它不是
“把所有事务历史压成备份”,也不是备份完成点。
在线备份通常在 checkpoint 起点建立 redo requirement:
checkpoint redo LSN
<= backup start LSN
<= copied data interval
<= backup stop LSN
<= selected recovery target
恢复必须拥有从要求的 redo/WAL 起点到目标的连续记录。
pg_backup_start / pg_backup_stop
低层协议的核心顺序:
SELECT pg_backup_start ( label => 'reviewed-label' , fast => false );
-- keep this session alive while the file copy runs
SELECT * FROM pg_backup_stop ( wait_for_archive => true );
关键语义:
调用 pg_backup_start 的连接必须保持;
fast=true 请求立即 checkpoint,可能增加 I/O;
文件复制期间数据库可以继续写;
pg_backup_stop 产生重要的 backup_label / tablespace_map 内容;
primary 上默认等待所需 WAL 归档;
自制工具必须逐步验证,不能只在最后看 exit code。
正式系统通常使用 pg_basebackup 或 pgBackRest,而不是重新实现低层协议。
理解协议是为了判断工具证据。
backup_label 不是备注
backup_label 告诉恢复过程:
which backup session
start WAL file
start LSN
checkpoint location
start time
它和 tablespace mapping 是恢复输入,不是方便人看的注释。随意修改、漏拷或
把另一份备份的 label 混入,都会破坏血缘。
PostgreSQL 12 以后在线备份 label 通常作为 pg_backup_stop 输出交给备份工具,
而不是永久留在运行中 primary 的数据目录。不要照搬旧版“拷走
backup_label 文件”的手册。
pg_basebackup
pg_basebackup 通过复制协议取得运行中 cluster 的物理基础备份。常见能力:
plain or tar output
server/client compression
backup manifest
WAL streaming or fetch
rate limiting
tablespace mapping
standby signal/config generation
checkpoint mode
progress reporting
它需要具备 REPLICATION 权限或 superuser,并满足 pg_hba.conf 与
max_wal_senders。它只备份整个 cluster,不能只备份一个 database。
示意:
pg_basebackup \
--host= source \
--pgdata= /safe/new/path \
--format= plain \
--wal-method= stream \
--checkpoint= fast \
--progress \
--verbose
这不是本章 formal executor;正式实验使用 Pigsty 已交付的 pgBackRest。
--wal-method
概念上:
none
backup output 不带 WAL;必须另有完整 archive
fetch
在备份末尾从 source pg_wal 取所需 WAL;
必须确保 WAL 未被回收
stream
另开 replication connection 同步流式接收;
需要额外 wal sender
即使 backup 包含让自身达到一致点的 WAL,若要恢复到更晚时间,仍需要后续
连续 archive。
backup manifest 与校验
manifest 记录文件、大小、checksum、WAL range 等,可用
pg_verifybackup 验证一份 pg_basebackup:
pg_verifybackup /path/to/basebackup
它能证明文件与 manifest 一致、所需 WAL 结构满足其检查,却仍不能证明:
repository credential 在事故时可用;
PostgreSQL 能在目标环境启动;
target 之后/之前的业务边界正确;
extension/OS/配置依赖齐全;
服务能够切换。
校验是 restore proof 的一层,不是替代。
data checksum 的边界
PostgreSQL data checksum 可以在读页时发现某些 page corruption。备份工具也
可对仓库对象做 checksum。两者都重要,但:
checksum matches
means bytes match expected checksum
checksum matches
does not mean business values are semantically correct
错误事务生成的页面 checksum 完全正确。
tablespace 与外部路径
物理备份必须覆盖 tablespace。恢复环境要检查:
pg_tblspc symlink;
target path 是否存在、为空、owner 正确;
不同主机路径是否需要 remap;
mount 是否真的是预期设备;
tablespace 与数据目录是否落在同一快照 consistency group。
忽略 tablespace 常导致“主数据目录恢复成功,启动时才发现一半对象缺失”。
从 standby 备份
从 replica 取备份可降低 primary I/O,但带来不同限制:
备份期间 standby 不能被 promote;
backup 与 primary timeline/WAL archive 仍要协调;
restartpoint 不等于 primary checkpoint;
低活动时某些增量条件可能不成立;
replication lag 影响备份包含的历史;
工具必须知道如何从正确节点取得并验证 WAL。
“从副本备份”是容量/可用性选择,不自动增加备份独立性。
PostgreSQL 18 原生增量
PostgreSQL 18 支持:
pg_basebackup --incremental= /path/to/prior/backup_manifest ...
服务端依据 pg_wal/summaries 中的 WAL summary 判断改变的 block。恢复前用
pg_combinebackup 把 full 与后续 incremental 合成为可启动的 synthetic
full。
依赖链:
prior full
-> prior/current manifests
-> every required intermediate incremental
-> WAL summaries at backup time
-> pg_combinebackup
-> normal WAL recovery
PostgreSQL 不会替你管理哪些旧备份仍被新 incremental 依赖。过期策略删除
一个祖先,就可能让后代全部不可恢复。
版本兼容
物理备份通常用于同一 PostgreSQL 大版本。恢复环境还要匹配:
CPU architecture and page format expectations
PostgreSQL major
extension shared libraries
collation/locale providers
tablespace layout
configuration parameters needed during recovery
本章开发实验故意把 max_connections 从 source 的 500 降到 20,
PostgreSQL 18 拒绝恢复:
recovery aborted because of insufficient parameter settings
max_connections = 20 is lower than on the primary, where it was 500
这不是性能调优问题,而是 recovery safety check。正式 runner 携带 source 的:
max_connections
max_worker_processes
max_wal_senders
max_prepared_transactions
max_locks_per_transaction
不能因为“验证实例很小”就任意降低 WAL 所要求的上限。
21.2.2 归档、timeline history 与恢复链
WAL 是有序的物理历史
WAL record 在数据页落盘前耐久化,支持 crash recovery、streaming
replication 与 archive recovery。LSN 是逻辑日志位置,例如:
通常每个 segment 16 MiB,但 segment size 可在 initdb 时选择。WAL 文件名
编码 timeline、log 与 segment,不应靠截字符串之外的自造规则做跨配置运算;
使用 PostgreSQL 函数:
SELECT pg_current_wal_lsn ();
SELECT pg_walfile_name ( pg_current_wal_lsn ());
SELECT pg_wal_lsn_diff ( pg_current_wal_lsn (), '0/20000000' );
SELECT pg_switch_wal ();
连续性要求
一份 base backup 要恢复到目标 t t t ,必须有:
[ L S N r e q u i r e d s t a r t , L S N t ]
[LSN_{required\ start}, LSN_t]
[ L S N re q u i re d s t a r t , L S N t ] 上的每一段 WAL,以及中途 timeline 切换需要的 history。
缺一段不会得到“少几秒数据”;恢复通常停在 gap:
base -> WAL A -> WAL B -> [missing C] -> WAL D -> target
即使 D 在仓库里,也不能跨过 C。
archive_mode、archive_command 与 archive_library
归档要求:
wal_level >= replica
archive_mode = on
archive_command or archive_library configured
shell archive command 中:
%p source path relative to data directory
%f WAL filename
最重要的返回值合同:
exit 0
PostgreSQL believes WAL is durably archived and may recycle local file
nonzero
PostgreSQL retries
错误地返回 0 是数据丢失风险;持续返回非零则形成 backlog 与磁盘风险。
归档必须幂等,但不能覆盖不同内容
crash 后 PostgreSQL 可能再次提交同一个 WAL 文件。正确 archive sink 应:
target absent
atomically write and durably persist
target exists with identical content
return success
target exists with different content
return failure and alert
绝不能无条件覆盖。两个不同 system identifier 的 cluster 若误用同一 archive
namespace,可能产生相同 WAL 文件名却内容不同。
stanza、repository path、cluster identity 和权限隔离是防碰撞设计的一部分。
timeline 为什么存在
一次恢复或 promote 会从旧历史分叉:
timeline 7: A -> B -> C -> D
\
timeline 8: C' -> E -> F
新 timeline 保留父 timeline 与分叉 LSN 的 history。这样旧历史不会被
“回到过去再向前写”覆盖。
需要区分:
system identifier
initdb 生成的物理 cluster 血缘标识
timeline
同一物理血缘中的历史分支
checkpoint timeline
最近 checkpoint 记录的 timeline
current WAL timeline
当前写入 WAL filename 所在 timeline
恢复出来的物理副本应与 source system identifier 相同;完成 promotion 后
应进入一个新 timeline。本章 evidence 只记录关系:
system identifier relation = matches source
timeline 7 -> 8
不公开原始 identifier。
recovery 输入
现代 PostgreSQL 通过 signal file 进入恢复:
recovery.signal
archive recovery / PITR
standby.signal
standby mode, can continue waiting for WAL/stream
常见设置:
restore_command = 'fetch %f into %p'
recovery_target_time = '...+00'
recovery_target_name = '...'
recovery_target_xid = '...'
recovery_target_lsn = '...'
recovery_target_inclusive = on|off
recovery_target_timeline = 'latest'
recovery_target_action = 'pause'|'promote'|'shutdown'
同一轮只能选择一种 target kind。没有 target 时通常重放到可用 WAL 尾部。
命名点:
SELECT pg_create_restore_point ( 'before_risky_change' );
只在 primary 上有意义,并写入 WAL。它不是 backup;没有 base 与 archive,
名字本身什么也恢复不了。
inclusive / exclusive
恢复到 time、XID、LSN 时,要明确目标 record/transaction 是否包含。误删场景
常想恢复到 destructive transaction 之前 :
target transaction known
recovery_target_xid = destructive xid
recovery_target_inclusive = false
但必须在实验中验证具体 target type 的语义。不要只凭自然语言“到某时刻”。
target timeline
恢复链可能包含多次 promote。latest 让恢复沿 archive 中可达的最新 timeline
继续;current/具体数字会限制选择。
危险例子:
choose an old backup
target time belongs to a child timeline
force target_timeline=current
结果可能根本到不了目标,或走错历史。选择前应画出 lineage:
backup timeline
-> history file
-> parent fork LSN
-> target timeline
hot standby 到达 consistent state 后可先接受只读连接,然后才到 target 并
执行 action。本章实际日志顺序:
consistent recovery state reached
database system is ready to accept read-only connections
recovery stopping at restore point
selected new timeline ID: 8
archive recovery complete
end-of-recovery checkpoint
database system is ready to accept connections
因此完成条件应为:
SELECT pg_is_in_recovery (); -- must be false
SHOW transaction_read_only ; -- must be off
BEGIN ;
CREATE TEMP TABLE write_probe ( x int );
INSERT INTO write_probe VALUES ( 1 );
ROLLBACK ;
只读 SELECT 成功不是 promotion proof。
配置文件不在 WAL 历史里
恢复到过去不会自动恢复:
postgresql.conf external changes
pg_hba.conf
pg_ident.conf
Patroni YAML/DCS policy
TLS certificates
Pigsty inventory
DNS/proxy config
application secrets
这些必须由 versioned declaration、配置备份与平台自动化重建。把配置也塞进
data directory 不是充分答案,因为事故可能同时损坏或需要在新环境重写。
21.2.3 复制槽、归档失败与 WAL 保留者
谁让 pg_wal 不能回收
WAL retention 的常见“持有人”:
checkpoint/recovery requirement
archive not yet successful
physical replication slot
logical replication slot
wal_keep_size
backup in progress
standby/restartpoint needs
它们并不是同一种保护,也不能相互替代。
archive backlog
当 archive command 失败:
completed segment remains needed
-> retry
-> pg_wal grows
-> filesystem full
-> PostgreSQL PANIC/offline
数据库继续运行一段时间不代表故障无害。官方文档指出,pg_wal 所在文件系统
填满会导致 PANIC;事务不会因此神奇地归档到远端。
监控:
SELECT archived_count ,
last_archived_wal ,
last_archived_time ,
failed_count ,
last_failed_wal ,
last_failed_time ,
stats_reset
FROM pg_stat_archiver ;
解释要注意:
failed_count = 21
last successful archive is after last failure
可能表示历史上失败过、当前已经恢复。不能仅因累计 count > 0 就报“当前失败”。
反之,长时间没有新 WAL 的系统,last_archived_time 老也未必故障。结合:
current WAL segment;
archive queue/backlog;
WAL generation rate;
repository maximum;
disk free;
recent command errors。
archive lag 与数据风险
两个间隙:
operational backlog
current WAL - last archived WAL
disaster data gap
source lost时,last business truth - last independently durable WAL
可用 byte 估计:
SELECT pg_wal_lsn_diff (
pg_current_wal_lsn (),
'last known archived LSN'
);
但 repository 通常按 segment 报告,边界内还有 partially filled segment。
生产 RPO 应用时间和业务 token 做补充。
为什么低流量也有 archive delay
archive command 通常对完成的 segment 工作。若写入很少:
important transaction commits
segment remains open
no archive object yet
选择:
archive_timeout 定期强制 switch;
关键变更后 pg_switch_wal();
pgBackRest check 触发/验证;
streaming WAL 到独立系统。
代价是更多 segment object 和带宽。策略应由 RPO 与成本反推。
physical replication slot
physical slot 保护某个 consumer 尚未接收的 WAL:
SELECT slot_name ,
slot_type ,
active ,
restart_lsn ,
wal_status ,
safe_wal_size ,
inactive_since
FROM pg_replication_slots ;
如果 consumer 永久消失而 slot 保留,WAL 可无限增长,除非
max_slot_wal_keep_size 设限。设限后 slot 也可能变为不可继续,需重建
replica。
槽保护 streaming consumer,不等于 archive 成功:
slot retained WAL on primary disk
!= independent disaster copy
logical slot 更容易被忽视
logical slot 还关联 catalog horizon:
保留 WAL;
可能保留 dead tuples/catalog rows;
consumer lag 影响磁盘与 vacuum;
failover/同步 slot 有版本与配置前提。
监控 slot 要看:
active
restart_lsn
confirmed_flush_lsn
wal_status
safe_wal_size
xmin/catalog_xmin
owner and consumer
没有 owner 的 slot 是容量事故候选。
wal_keep_size
wal_keep_size 是最近 WAL 的最低保留量,帮助无 slot standby 应对短暂断开。
它:
不是硬上限;
不保证某个 consumer 的准确位置;
不写远端仓库;
不提供长期 PITR;
不替代 slot 或 archive。
多个保留者叠加
最终 pg_wal 需要保留到最老需求:
L S N r e c y c l e f r o n t i e r = min ( L S N c h e c k p o i n t , L S N a r c h i v e , L S N s l o t s , L S N b a c k u p , L S N s t a n d b y )
LSN_{recycle\ frontier}
=\min(
LSN_{checkpoint},
LSN_{archive},
LSN_{slots},
LSN_{backup},
LSN_{standby}
)
L S N recyc l e f ro n t i er = min ( L S N c h ec k p o in t , L S N a rc hi v e , L S N s l o t s , L S N ba c k u p , L S N s t an d b y ) 概念上谁最老,谁控制回收边界。因此磁盘告警时不要只看“archive 正常”,
还要看所有 slot、backup lock 与 standby 状态。
本章沙箱事实
正式演练前后观察到:
archive_mode=on
archive command configured
repository stanza status=ok
one S3-compatible repository
target restore-point segment <= repository maximum segment
source timeline remains 7
restore timeline becomes 8
累计 archiver 历史包含早期失败,但最近成功晚于最后失败;本章没有把累计
失败数误判为当前中断。
故障处理顺序
archive backlog:
1. stop unsafe cleanup/expiration
2. measure pg_wal free space and growth rate
3. identify exact archive error and repository availability
4. preserve source WAL
5. restore archive path/credential/capacity
6. confirm backlog drains and repository maximum advances
7. prove a restore target, not just count successes
8. write incident and prevention
不要先:
delete pg_wal files
drop unknown slots
reset stanza
expire repository aggressively
这些动作可能把可恢复性问题变成不可恢复。
本节原生检查单
SHOW wal_level ;
SHOW archive_mode ;
SHOW archive_command ; -- do not copy secrets into tickets/evidence
SHOW archive_timeout ;
SHOW wal_keep_size ;
SHOW max_slot_wal_keep_size ;
SELECT * FROM pg_stat_archiver ;
SELECT slot_name , slot_type , active , restart_lsn ,
confirmed_flush_lsn , wal_status , safe_wal_size
FROM pg_replication_slots ;
SELECT pg_current_wal_lsn (),
pg_walfile_name ( pg_current_wal_lsn ());
SELECT system_identifier
FROM pg_control_system (); -- compare securely; do not publish raw id
SELECT timeline_id , redo_lsn , checkpoint_lsn
FROM pg_control_checkpoint ();
在 replica 上不能调用 primary-only current WAL 函数;使用 replay/receive
位置并标注 observation point。
小结
物理 recoverability 的核心不是“有一个 tar 包”,而是:
one reviewed physical lineage
+ complete base image
+ exact backup metadata
+ continuous WAL
+ reachable timeline history
+ compatible recovery runtime
下一节把这些对象放进仓库依赖图:full、diff、incr 应如何保留,怎样防止
正确的自动过期删除仍被后代依赖的祖先,以及为什么加密不等于不可变。
上一节:从恢复场景设计备份 · 返回本章目录 · 下一节:备份仓库与保留策略 ·
查看全书目录 · 查看索引中心