备份机制

pgBackRest 的概念体系(stanza、仓库、备份链、保留策略、时间线)与 Pigsty 封装层的参数映射:理解每条命令背后发生了什么。

Pigsty 的备份恢复功能,最终都会落实为 pgBackRest 命令的执行。 所以要真正掌握这套系统,需要理解两件事:pgBackRest 本身的概念体系(stanza、仓库、备份链、保留、时间线), 以及 Pigsty 各层封装如何映射到它(参数如何变成命令行选项)。本页依次讲清这两部分。


pgBackRest 核心概念

Stanza:集群的备份身份

Stanza(节)是 pgBackRest 中一套 PostgreSQL 集群备份配置的名称,也是仓库内隔离不同集群的命名空间。 Pigsty 将 stanza 直接映射为集群名 pg_cluster: 集群 pg-meta 的备份就存储在仓库的 backup/pg-meta/archive/pg-meta/ 目录下, 多套集群可以安全共享同一个仓库。

Stanza 记录着源集群的 system-id 与主版本号,写入备份前会核对身份 —— 这正是 克隆集群 后需要 stanza-upgrade 善后的原因。 Stanza 由 stanza-create 创建(Pigsty 在集群初始化时自动完成), 版本升级后用 stanza-upgrade 更新。

仓库:备份存放在哪里

仓库(Repository)是备份与 WAL 归档的存储后端,在配置中以 repo1-* 系列选项定义: repo1-type 决定类型(POSIX 文件系统、S3、Azure、GCS、SFTP),repo1-path 决定路径, repo1-cipher-* 决定加密,repo1-retention-* 决定保留策略。 Pigsty 通过 pgbackrest_repo 参数生成这些配置,详见 备份仓库

备份链与备份标签

pgBackRest 支持三种备份类型,后两种依赖前面的备份构成 备份链

类型内容标签后缀
全量备份(full)完整复制数据库集群F
差异备份(diff)相对最近一次 全量 的变化D
增量备份(incr)相对最近一次 任意备份 的变化I

每个备份都有唯一的 备份标签(label),命名规则本身就描述了备份链: 20250715-013657F 是一个全量备份,20250715-013657F_20250715-013724D 是基于它的差异备份, 20250715-013657F_20250715-013730I 是增量备份 —— 下划线前的部分标识它所依附的全量备份。 恢复时用 --set 指定从哪个备份集开始还原,默认自动选择目标时间点前最近的一个。

保留策略:仓库如何不被撑爆

保留策略(Retention)决定旧备份何时被清除。repo1-retention-full 配合 repo1-retention-full-typecount 按份数 / time 按天数)控制全量备份的保留; 全量备份过期时,依附它的差异/增量备份与对应的 WAL 归档一并清除。 Pigsty 默认启用 expire-auto,每次备份完成后自动执行过期清理,也可用 expire 命令手动触发。

time 表示 最短时间窗口,不是“只保留最近 N 天内的全量备份”。只有在仓库中还存在另一份年龄达到 N 天的全量备份时, 更老的全量备份才会过期。因此 retention_full: 14 配合每周全备,稳态会保留三条全量备份链,恢复窗口约为 14~21 天。

WAL 归档:保留下来的历史

PostgreSQL 的 archive_command 在每个 WAL 段写满(或 archive_timeout 超时)后触发, Pigsty 将其配置为 archive-push,把 WAL 推入仓库; 恢复时则由 restore_command 调用 archive-get 按需拉取。 Pigsty 启用了异步归档(archive-async=y),经由 /pg/spool 假脱机目录批量推送,避免归档拖慢主库。

时间线:分叉的历史

每次恢复提升(或故障切换)都会开启一条新的 时间线(Timeline),旧时间线的 WAL 仍保留在仓库中。 恢复时可用 --target-timeline 指定沿哪条时间线重放(默认 latest), 这使得"恢复错了再恢复一次"成为可能。完整心智模型见概念层 工作原理

恢复机制:restore 命令做了什么

理解 restore 命令的行为,就理解了 PITR 的执行过程。它做两件事:

  1. 重建数据目录:从备份集还原数据文件。带 --delta 选项(Pigsty 默认启用)时执行增量还原 —— 校验现有文件,只重写与备份不一致的部分,大幅缩短大库的还原时间。
  2. 写入恢复配置:生成 recovery.signal 标记与恢复参数(restore_commandrecovery_target_*), 使 PostgreSQL 下次启动时进入恢复模式,从仓库拉取 WAL 重放至目标点。

因此 restore 命令返回成功只是完成了一半:真正的恢复发生在 PostgreSQL 启动之后的 WAL 重放阶段。 重放到达目标后的行为由 --target-action 决定:pause 暂停等待检查、promote 提升开启新时间线、shutdown 停机。

恢复目标的参数组合是固定句式:--type 指定目标类型,--target 给出目标值, 可选的 --target-exclusive 控制边界、--target-timeline 选择时间线、--set 指定起点备份:

pgbackrest --stanza=pg-meta restore                                        # 恢复到 WAL 归档末尾
pgbackrest --stanza=pg-meta --type=immediate restore                       # 恢复到最近一致点
pgbackrest --stanza=pg-meta --type=time --target='2025-07-13 10:00:00+00' restore
pgbackrest --stanza=pg-meta --type=xid  --target='250000' --target-exclusive restore
pgbackrest --stanza=pg-meta --type=name --target='my-restore-point' restore
pgbackrest --stanza=pg-meta --type=lsn  --target='0/4001C80' --target-action=promote restore

实际观察

您可以使用 pg_dbsu 用户(默认 postgres)直接执行 pgbackrest 命令, 观察上述概念的实际形态 —— 注意备份标签的命名、备份大小的差异,以及 info 输出中的备份链引用关系:

$ pgbackrest --stanza=pg-meta --type=full backup
2025-07-15 01:36:57.007 P00   INFO: backup command begin 2.54.2: --annotation=pg_cluster=pg-meta ...
2025-07-15 01:36:57.030 P00   INFO: execute non-exclusive backup start: backup begins after the requested immediate checkpoint completes
2025-07-15 01:36:57.105 P00   INFO: backup start archive = 000000010000000000000006, lsn = 0/6000028
2025-07-15 01:36:58.540 P00   INFO: new backup label = 20250715-013657F
2025-07-15 01:36:58.588 P00   INFO: full backup size = 44.5MB, file total = 1437
2025-07-15 01:36:58.589 P00   INFO: backup command end: completed successfully (1584ms)
$ pgbackrest --stanza=pg-meta --type=diff backup
2025-07-15 01:37:24.952 P00   INFO: backup command begin 2.54.2: ...
2025-07-15 01:37:24.985 P00   INFO: last backup label = 20250715-013657F, version = 2.54.2
2025-07-15 01:37:26.337 P00   INFO: new backup label = 20250715-013657F_20250715-013724D
2025-07-15 01:37:26.381 P00   INFO: diff backup size = 424.3KB, file total = 1437
2025-07-15 01:37:26.381 P00   INFO: backup command end: completed successfully (1431ms)
$ pgbackrest --stanza=pg-meta --type=incr backup
2025-07-15 01:37:30.305 P00   INFO: backup command begin 2.54.2: ...
2025-07-15 01:37:30.337 P00   INFO: last backup label = 20250715-013657F_20250715-013724D, version = 2.54.2
2025-07-15 01:37:31.356 P00   INFO: new backup label = 20250715-013657F_20250715-013730I
2025-07-15 01:37:31.403 P00   INFO: incr backup size = 8.3KB, file total = 1437
2025-07-15 01:37:31.403 P00   INFO: backup command end: completed successfully (1099ms)
$ pgbackrest --stanza=pg-meta info
stanza: pg-meta
    status: ok
    cipher: aes-256-cbc

    db (current)
        wal archive min/max (17): 000000010000000000000001/00000001000000000000000A

        full backup: 20250715-013657F
            timestamp start/stop: 2025-07-15 01:36:57+00 / 2025-07-15 01:36:58+00
            wal start/stop: 000000010000000000000006 / 000000010000000000000006
            database size: 44.5MB, database backup size: 44.5MB
            repo1: backup size: 8.7MB

        diff backup: 20250715-013657F_20250715-013724D
            timestamp start/stop: 2025-07-15 01:37:24+00 / 2025-07-15 01:37:26+00
            database size: 44.5MB, database backup size: 424.3KB
            repo1: backup size: 94KB
            backup reference total: 1 full

        incr backup: 20250715-013657F_20250715-013730I
            timestamp start/stop: 2025-07-15 01:37:30+00 / 2025-07-15 01:37:31+00
            database size: 44.5MB, database backup size: 8.3KB
            repo1: backup size: 504B
            backup reference total: 1 full, 1 diff

Pigsty 的封装层次

在原生命令之上,Pigsty 提供了逐层升高的封装,每一层都只是对下一层的参数化调用,没有黑魔法:

层次接口它实际做什么
集群编排pg_pitr + pgsql-pitr.yml编排整个集群的恢复:暂停 HA → 停库 → 渲染配置并调用 pgbackrest restore → 输出控制信息 → 清理 etcd → 拉起 HA
实例编排pig pitr单节点编排:预检 → 停 Patroni/PG → 调用 pgbackrest restore → 可选启动 PG,Patroni 保持停止
命令原语pig pb / pb 别名 / pg-backup自动填充 --stanza 与 DBSU 身份,转发给 pgbackrest 对应子命令
底层引擎pgbackrest读取 /etc/pgbackrest/pgbackrest.conf,实际执行备份/归档/恢复

命令原语层

pb 是登录 shell 内置的别名函数:从配置文件解析出 stanza 后转发,让您少敲一个参数:

function pb() {
    local stanza=$(grep -o '\[[^][]*]' /etc/pgbackrest/pgbackrest.conf | head -n1 | sed 's/.*\[\([^]]*\)].*/\1/')
    pgbackrest --stanza=$stanza $@
}
pb info     # = pgbackrest --stanza=pg-meta info
pb backup   # = pgbackrest --stanza=pg-meta backup

pg-backup 脚本在此基础上增加了 角色检查(只在主库执行,从库直接退出),供 crontab 安全调用:

pg-backup full   # = pgbackrest --stanza=pg-meta --type=full backup(仅主库执行)
pg-backup diff   # = pgbackrest --stanza=pg-meta --type=diff backup
pg-backup incr   # = pgbackrest --stanza=pg-meta --type=incr backup(默认,无全量时自动升级为全量)

pig pb 提供了更完善的封装:自动检测 stanza、自动以 DBSU 身份执行(root 下 su、普通用户 sudo)、 备份前检查主库角色、恢复前显示执行计划并要求确认。完整子命令表见 管理命令

参数如何映射

Pigsty 恢复接口的每个参数,都对应一个 pgbackrest 选项 —— 三层接口共用同一套语义:

pg_pitr 字段pig pitr 选项pgbackrest 选项含义
cluster--stanza--stanza从哪个集群的备份恢复(源 stanza)
type + time/xid/lsn/name--time/--xid/--lsn/--name--type + --target恢复目标
(type: default)--default不传 --type/--target重放到 WAL 归档末尾
(type: immediate)--immediate--type=immediate恢复到最近一致点
exclusive--exclusive / -X--target-exclusive停在目标之前(排除目标点)
action--target-action--target-action到达目标后:pause / promote / shutdown
timeline--target-timeline / -T--target-timeline目标时间线,默认 latest
set--set / -b--set从哪个备份集开始还原
db_include / db_exclude--db-include / --db-exclude选择性恢复部分数据库
link_map--link-map表空间/目录软链接映射
processprocess-max恢复并行进程数
data--data / -D--pg1-path恢复到哪个数据目录
reporepo1-*(渲染临时配置)覆盖备份仓库定义;pig pitr --repo 仅选择现有配置中的仓库编号

选择性恢复仍是物理恢复:未包含或被排除的数据库会以稀疏清零文件还原,以便 PostgreSQL 完成恢复,但这些数据库本身不可访问, 恢复后需要显式删除。它不是 pg_dump 那样的逻辑子集恢复。

配置如何渲染

pgbackrest_repo 中被 pgbackrest_method 选中的仓库定义,会按简单规则渲染进 /etc/pgbackrest/pgbackrest.conf: 键名的下划线替换为连字符,加上 repo1- 前缀 —— 因此 pgBackRest 支持的仓库级配置项 可以直接写入参数:

pgbackrest_repo:
  minio:
    type: s3                  # ----> repo1-type=s3
    s3_endpoint: sss.pigsty   # ----> repo1-s3-endpoint=sss.pigsty
    cipher_type: aes-256-cbc  # ----> repo1-cipher-type=aes-256-cbc
    retention_full: 14        # ----> repo1-retention-full=14

执行 pgsql-pitr.yml 时,Pigsty 会另行渲染一份临时配置 /pg/conf/pitr.conf(避免污染常规配置), 该剧本启动恢复期间的 PostgreSQL 日志写入 /pg/tmp/recovery.log


定时备份

Pigsty 使用 Linux crontab 调度备份任务:pg_crontab 参数中的条目 会写入 postgres 用户的 crontab。因为 pg-backup 自带角色检查,同一份 crontab 可以下发到集群所有节点 —— 故障切换后,新主库会自动接续后续定时备份。

pg_crontab: [ '00 01 * * * /pg/bin/pg-backup full' ]
pg_crontab:
  - '00 01 * * 1 /pg/bin/pg-backup full'
  - '00 01 * * 2,3,4,5,6,7 /pg/bin/pg-backup'

修改后用剧本应用变更:

./pgsql.yml -t pg_crontab -l pg-meta    # 更新 pg-meta 集群的 crontab

如何选择备份频率与保留策略,请参阅 备份策略


部署细节

pgBackRest 组件在 pgsql.yml 剧本中完成安装与配置:

  • pg_packages 中的 pgsql-common 组安装,二进制位于 /usr/bin/pgbackrest
  • pg_backup 子任务负责渲染配置、创建 stanza;由 pgbackrest_enabled 控制(默认启用)
  • 集群初始化后默认尝试执行一次 初始全量备份,留下 /etc/pgbackrest/initial.done 标记防止重复; 由 pgbackrest_init_backup 控制

文件层次

路径用途
/usr/bin/pgbackrest二进制,来自 PGDG 仓库的 pgbackrest
/etc/pgbackrest/pgbackrest.conf主配置文件(stanza + 仓库定义)
/pg/backup本地仓库数据目录(local 仓库时使用)
/pg/spool异步归档的假脱机目录
/pg/log/pgbackrest/备份/归档/恢复日志,pgbackrest_log_dir
/pg/conf/pitr.confPITR 过程中的临时 pgbackrest 配置
/pg/tmp/recovery.logPITR 过程中的 PostgreSQL 恢复日志

监控

每个节点运行 pgbackrest_exporter 服务(端口 pgbackrest_exporter_port9854), 将备份状态导出为监控指标(参见 pgBackRest 监控指标)。 可通过 pgbackrest_exporter_options 定制, 或将 pgbackrest_exporter_enabled 设为 false 禁用。