这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

备份恢复

配置备份策略与备份仓库,管理备份,执行时间点恢复:pgBackRest 引擎与 Pigsty 封装层的完整实操手册。

Pigsty 使用 pgBackRest 管理 PostgreSQL 备份 —— 这可能是 PostgreSQL 生态中最强大的开源备份工具, 支持增量备份、并行处理、加密、MinIO/S3 对象存储等众多特性。 每个 PGSQL 集群默认都预配置了备份与 WAL 归档,开箱即用。

本章是备份恢复的 实操手册:配置方法、管理命令、恢复操作与演练教程。 设计理念与心智模型(为什么、如何权衡)请参阅概念层文档 时间点恢复

所有的备份恢复操作,最终都会落实为 pgBackRest 命令。Pigsty 在它之上提供了三层封装,按需选用:

层次接口形态适用场景
集群编排pg_pitr 参数 + pgsql-pitr.yml 剧本Ansible 剧本生产集群恢复:编排 HA、etcd 与多节点
实例编排pig pitr命令行工具单节点恢复:在数据库节点上直接编排执行
命令原语pig pb / pb 别名 / pg-backup 脚本pgBackRest 封装备份、查询、清理,以及非托管实例的裸恢复
底层引擎pgbackrest原生命令一切封装的最终执行者
章节内容
机制pgBackRest 核心概念(stanza / 仓库 / 保留 / 时间线)与 Pigsty 的封装映射
策略设计备份策略:调度计划、恢复窗口与磁盘空间规划
仓库配置备份仓库:本地、MinIO、S3,加密、版本控制与锁定
管理备份管理命令手册:启停、手动备份、查看、清理、Stanza 管理
恢复执行时间点恢复:恢复目标、分步执行、参数参考
克隆用 PITR 克隆数据库集群:找回数据、恢复演练
示例沙箱教程:使用 pgBackRest 原语手工执行恢复

快速上手

  1. 设计备份策略:用 pg_crontab 声明定时备份计划,用 pgbackrest_repo 选择备份仓库
  2. 管理备份:用 pg-backup 手动备份,用 pb info 查看备份状态
  3. 执行恢复:用 pg_pitr 参数声明恢复目标,运行 pgsql-pitr.yml 剧本
pg_crontab: [ '00 01 * * * /pg/bin/pg-backup full' ]
./pgsql-pitr.yml -l pg-meta -e '{"pg_pitr": { "time": "2025-07-13 10:00:00+00", "action": "promote" }}'

1 - 备份机制

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 禁用。

2 - 备份策略

设计并配置备份策略:备份频率决定恢复速度,保留策略决定恢复窗口与空间占用,两张推演图帮您做出量化决策。

备份策略要回答三个问题:何时 备份(调度计划)、何处 存放(备份仓库)、 保留多久(保留策略)。本页给出两套久经考验的预设策略及其量化推演 —— 背后的权衡逻辑请参阅概念层文档 策略权衡

备份频率与恢复速度直接相关:恢复时需要从最近的基础备份开始重放 WAL 日志到目标时间点, 备份越频繁,需要重放的 WAL 越少,恢复越快;而保留策略与仓库空间直接相关:窗口越长,占用空间越大。


每日全量备份

对于生产数据库,建议从最简单的每日全量备份策略开始。Pigsty 随附的标准 pigsty.yml 集群示例采用这一策略, 配合默认的 local 本地仓库(保留最近 2 个全量备份)使用:

pg_crontab: [ '00 01 * * * /pg/bin/pg-backup full' ]
pgbackrest_method: local          # 选择备份仓库方法:`local`、`minio` 或其他自定义仓库
pgbackrest_repo:                  # pgbackrest 仓库配置: https://pgbackrest.org/configuration.html#section-repository
  local:                          # 使用本地 POSIX 文件系统的默认 pgbackrest 仓库
    path: /pg/backup              # 本地备份目录,默认为 `/pg/backup`
    retention_full_type: count    # 按数量保留全量备份
    retention_full: 2             # 使用本地文件系统仓库时,保留2个,最多3个全量备份

假设您的数据库大小为 100GB,每天写入 10GB 数据,备份耗时 1 小时。 下图将「恢复窗口」与「存储空间占用」合并到同一时间轴(0~108h)上推演该策略的稳态行为: 恢复窗口在 24~48 小时 之间循环,备份占用约为 2 个全量备份加上 1~2 天的 WAL 归档。 在实践中,您需要准备至少 3~5 倍 数据库大小的备份磁盘,才能从容使用该默认策略。


全量 + 增量备份

如果使用 MinIO / S3 作为集中式备份仓库,存储空间不再受本地磁盘限制, 此时可以用「周全量 + 每日增量」配合两周保留策略,换取更长的恢复窗口:

pg_crontab:  # 周一凌晨1点全量备份,其余每日增量备份
  - '00 01 * * 1           /pg/bin/pg-backup full'
  - '00 01 * * 2,3,4,5,6,7 /pg/bin/pg-backup'
pgbackrest_method: minio
pgbackrest_repo:                  # pgbackrest 仓库配置: https://pgbackrest.org/configuration.html#section-repository
  minio:                          # 可选的 minio 仓库
    type: s3                      # minio 兼容 S3 协议
    s3_endpoint: sss.pigsty       # minio 端点域名,默认为 `sss.pigsty`
    s3_region: us-east-1          # minio 区域,默认 us-east-1,对 minio 无实际意义
    s3_bucket: pgsql              # minio 桶名,默认为 `pgsql`
    s3_key: pgbackrest            # pgbackrest 的 minio 用户访问密钥
    s3_key_secret: S3User.Backup  # MinIO 用户密钥
    s3_uri_style: path            # minio 使用路径风格 URI 而非主机风格
    path: /pgbackrest             # minio 备份路径,默认为 `/pgbackrest`
    storage_port: 9000            # minio 端口,默认 9000
    storage_ca_file: /etc/pki/ca.crt  # minio CA 证书路径,默认 `/etc/pki/ca.crt`
    block: y                      # 启用块级增量备份
    bundle: y                     # 将小文件打包成单个文件
    bundle_limit: 20MiB           # 文件包大小限制,对象存储建议 20MiB
    bundle_size: 128MiB           # 文件包目标大小,对象存储建议 128MiB
    cipher_type: aes-256-cbc      # 为远程备份仓库启用 AES 加密
    cipher_pass: pgBackRest       # 仓库加密密码
    retention_full_type: time     # 按时间保留全量备份
    retention_full: 14            # 按时间保留 14 天

同样假设数据库 100GB、每日增量与 WAL 均按 10GB 粗估,下图推演 30 天内恢复窗口与存储占用的变化: retention_full_type: time 会确保至少留下一份年龄达到 14 天的全量备份,周全备时恢复窗口在 14~21 天 之间循环。 按这组未压缩假设,稳态占用约为 560~690GB,新全量完成、旧链过期前的瞬时峰值约为 790GB; 实际占用取决于 WAL 量、块级增量命中率与 zstd 压缩率。


空间规划

两套策略的空间需求可以按以下经验公式粗估(实际占用受压缩与块级增量影响,通常更低):

策略恢复窗口空间粗估建议预留
每日全量,保留 2 份(local)24~48 小时2 × 全量 + 1~2 天 WAL数据库大小的 3~5 倍
周全量 + 日增量,按时间保留 14 天(minio)14~21 天3 × 全量 + 12~18 份增量 + 14~21 天 WAL按实测压缩率与 WAL 量规划

注意 瞬时峰值:新的全量备份成功完成后,旧备份才会参与过期计算,因此仓库会短暂多出一份全量备份, 同时保留尚未清理的旧备份链与 WAL。空间规划必须覆盖这个峰值而非只看清理后的稳态。

WAL 归档的增长与写入负载成正比。批量导数、VACUUM FULL、大规模 UPDATE 都会瞬间产生大量 WAL, 如果备份仓库空间紧张,请在此类操作前后关注仓库水位(监控指标 开箱即用)。


应用策略变更

备份策略的三类变更,分别用对应的剧本任务应用:

./pgsql.yml -t pg_crontab -l pg-meta    # 变更调度计划(pg_crontab)后:更新 crontab
./pgsql.yml -t pg_backup  -l pg-meta    # 变更仓库定义(pgbackrest_repo / pgbackrest_method)后:重新渲染配置并初始化 stanza
pg-backup full                          # 切换仓库后:立即执行一次全量备份,建立新仓库中的恢复起点

注意:切换 pgbackrest_method 到新仓库后,旧仓库中的备份不会自动迁移; 在新仓库完成首次全量备份之前,恢复窗口存在缺口。

3 - 备份仓库

配置备份存储仓库:本地磁盘、MinIO 与 S3 对象存储,保留策略、加密、版本控制与对象锁定。

备份存储在哪里,由两个参数决定:pgbackrest_repo 定义所有候选仓库, pgbackrest_method 选择实际使用哪一个。 仓库定义中的键值会 按固定规则 渲染为 pgbackrest 的 repo1-* 配置项, 因此 pgBackRest 支持的任何仓库选项 都可以直接写入。


默认仓库

Pigsty 预置了两个仓库定义:localminio

  • local默认选项,使用本地 /pg/backup 目录(软链接指向 pg_fs_backup/data/backups
  • minio:使用专用 MinIO 集群或任意 S3 兼容对象存储(Pigsty 支持,默认不启用)
pgbackrest_method: local          # 选择备份仓库方法:`local`、`minio` 或其他自定义仓库
pgbackrest_repo:                  # pgbackrest 仓库配置: https://pgbackrest.org/configuration.html#section-repository
  local:                          # 使用本地 POSIX 文件系统的默认 pgbackrest 仓库
    path: /pg/backup              # 本地备份目录,默认为 `/pg/backup`
    retention_full_type: count    # 按数量保留全量备份
    retention_full: 2             # 使用本地文件系统仓库时,保留2个,最多3个全量备份
  minio:                          # 可选的 minio 仓库
    type: s3                      # minio 兼容 S3 协议
    s3_endpoint: sss.pigsty       # minio 端点域名,默认为 `sss.pigsty`
    s3_region: us-east-1          # minio 区域,默认 us-east-1,对 minio 无实际意义
    s3_bucket: pgsql              # minio 桶名,默认为 `pgsql`
    s3_key: pgbackrest            # pgbackrest 的 minio 用户访问密钥
    s3_key_secret: S3User.Backup  # pgbackrest 的 minio 用户密钥
    s3_uri_style: path            # minio 使用路径风格 URI 而非主机风格
    path: /pgbackrest             # minio 备份路径,默认为 `/pgbackrest`
    storage_port: 9000            # minio 端口,默认 9000
    storage_ca_file: /etc/pki/ca.crt  # minio CA 证书路径,默认 `/etc/pki/ca.crt`
    block: y                      # 启用块级增量备份
    bundle: y                     # 将小文件打包成单个文件
    bundle_limit: 20MiB           # 文件包大小限制,对象存储建议 20MiB
    bundle_size: 128MiB           # 文件包目标大小,对象存储建议 128MiB
    cipher_type: aes-256-cbc      # 为远程备份仓库启用 AES 加密
    cipher_pass: pgBackRest       # AES 加密密码,默认为 'pgBackRest'
    retention_full_type: time     # 按时间保留全量备份
    retention_full: 14            # 按时间保留 14 天

两套仓库的预置策略有意不同:local 不加密、不打包、按份数保留,追求简单与恢复速度; minio 加密(AES-256-CBC)、打包(bundle)、块级增量(block)、按时间保留两周,面向生产容灾。


保留策略

如果只备份不清理,仓库迟早被撑爆。保留策略在仓库定义中声明,由 pgBackRest 在每次备份后自动执行清理 (全量备份过期时,依附它的差异/增量备份与对应 WAL 归档一并清除):

  • retention_full_type: count + retention_full: 2:按份数保留 —— 保留最近 2 个全量备份(新备份完成前短暂存在第 3 个)
  • retention_full_type: time + retention_full: 14:按时间保留 —— 至少留下一份年龄达到 14 天的全量备份后,才会删除更老的备份

保留策略与恢复窗口、空间占用的量化关系,请参阅 备份策略; 手动清理过期备份的方法,请参阅 管理命令


使用 MinIO 仓库

MinIO 是 Pigsty 内置支持的 S3 兼容对象存储,作为集中备份仓库使用时, 为备份提供独立于数据库主机的故障域。启用方式:部署 MinIO 集群,然后将备份方法切换为 minio

all:
  vars:
    pgbackrest_method: minio      # 使用 minio 作为默认备份仓库
  children:                       # 定义一个单节点 minio SNSD 集群
    minio: { hosts: { 10.10.10.10: { minio_seq: 1 }} ,vars: { minio_cluster: minio }}

Pigsty 的 MinIO 仓库预设通过域名(默认 sss.pigsty)和 HTTPS 端点访问对象存储, 并使用自签名 CA(/etc/pki/ca.crt)验证这条链路。 默认的 pgsql 桶与 pgbackrest 访问用户在 MinIO 集群初始化时自动创建。

对于严肃的生产部署,建议使用多节点 MinIO 集群(MNMD,纠删码容错),参阅 MinIO 配置


使用 S3 / 云对象存储

如果您只有一个节点,最有意义的备份策略就是使用云厂商的对象存储服务(AWS S3、阿里云 OSS 等), 以极低的成本获得异地容灾能力。定义一个新仓库并切换过去即可:

pgbackrest_method: s3             # 使用 'pgbackrest_repo.s3' 作为备份仓库
pgbackrest_repo:                  # pgbackrest 仓库配置: https://pgbackrest.org/configuration.html#section-repository

  s3:                             # 阿里云 OSS(S3 兼容)对象存储服务示例
    type: s3                      # oss 兼容 S3 协议
    s3_endpoint: oss-cn-beijing-internal.aliyuncs.com
    s3_region: oss-cn-beijing
    s3_bucket: <your_bucket_name>
    s3_key: <your_access_key>
    s3_key_secret: <your_secret_key>
    s3_uri_style: host            # 云厂商对象存储通常使用主机风格 URI
    path: /pgbackrest
    bundle: y                     # 将小文件打包成单个文件
    bundle_limit: 20MiB           # 文件包大小限制,对象存储建议 20MiB
    bundle_size: 128MiB           # 文件包目标大小,对象存储建议 128MiB
    cipher_type: aes-256-cbc      # 为远程备份仓库启用 AES 加密
    cipher_pass: pgBackRest       # AES 加密密码
    retention_full_type: time     # 按时间保留全量备份
    retention_full: 14            # 按时间保留 14 天

  local:                          # 保留默认本地仓库定义,便于随时切换
    path: /pg/backup
    retention_full_type: count
    retention_full: 2

除 S3 兼容存储外,pgBackRest 还支持以下后端,配置方式参阅官方用户指南:


多集群共享仓库

一个集中式仓库可以同时服务多套 PostgreSQL 集群:pgBackRest 用 stanza (即 pg_cluster)隔离各集群的备份与归档,互不干扰。 这也是 克隆集群 的基础 —— 新集群可以直接从共享仓库中读取源集群的备份进行恢复。

因此,请确保同一仓库下的集群名称 全局唯一,即使它们分属不同的部署环境。


仓库版本控制

对象存储的 版本控制(Versioning)为仓库提供同一存储系统内的版本保护:即使备份文件被覆盖或删除,历史版本仍有机会找回。 它与当前仓库共享同一故障域和管理平面,不能替代独立的异地或离线副本。 您可以在 minio_buckets 中为桶添加 versioning 标志启用:

minio_buckets:
  - { name: pgsql ,versioning: true }
  - { name: meta  ,versioning: true }
  - { name: data }

配合 pgBackRest 的 repo-target-time 选项, 甚至可以把整个仓库"回滚"到过去某一时刻的状态读取 —— 相当于对备份系统本身做时间点恢复。


仓库锁定

部分对象存储(S3、MinIO 等)支持 对象锁定(Object Lock / WORM):对对象版本配置保留模式与期限后, 锁定版本在保留期内不可修改、不可永久删除。这是对抗勒索攻击的重要防线,但普通删除仍可能写入 Delete Marker, 暂时隐藏当前对象;恢复时需要保留版本与版本管理能力。

minio_buckets 中添加 lock 标志,只会在创建桶时启用对象锁定能力与版本控制:

minio_buckets:
  - { name: pgsql ,lock: true }
  - { name: meta  ,versioning: true }
  - { name: data }

这一步还没有给新对象设置 WORM 保留期。您还需要在 MinIO 中使用 mcli retention set 或控制台配置默认的 GOVERNANCE / COMPLIANCE 模式与期限,并用 mcli retention info 验证。GOVERNANCE 可以被拥有 bypass 权限的主体绕过; COMPLIANCE 在期限内连 root 用户也不能解除。

锁定会改变 过期清理移除集群备份 的行为:pgBackRest 可以让对象在逻辑上过期,但被锁定的历史版本仍会占用空间,直到保留期结束。上线前应在测试桶中验证 备份、expire、删除标记清理和版本恢复的完整流程。


切换仓库

变更仓库定义或切换 pgbackrest_method 后,需要重新渲染配置、初始化 stanza,并尽快建立新仓库中的恢复起点:

./pgsql.yml -t pg_backup -l pg-meta   # 重新配置 pgbackrest 并创建 stanza
pg-backup full                        # 立即执行全量备份,建立新仓库中的第一个恢复点

旧仓库中的备份不会自动迁移,但在其保留期内仍可作为恢复来源使用(通过 pg_pitr.repo 指定)。

4 - 管理命令

备份管理命令手册:启用与移除备份,手动备份,查看与清理,Stanza 管理,日志排查,以及替代备份工具。

备份管理命令应以数据库超级用户(pg_dbsu,默认 postgres)身份在数据库节点上执行。 您可以按习惯选择三种等价的入口:

  • pig pbpig 命令行工具 的封装 —— 自动检测 stanza、自动切换 DBSU 身份、带安全检查,推荐使用
  • pb:登录 shell 的别名函数,自动填充 --stanza 后转发给 pgbackrest
  • pgbackrest:原生命令,完整选项参阅 pgBackRest 命令参考

命令一览

pig 命令别名对应 pgbackrest 命令说明
pig pb infoiinfo查看备份与归档状态
pig pb listls列出仓库中的 stanza 与备份
pig pb backup [full/diff/incr]bbackup执行备份(自动检查主库角色)
pig pb restorerrestore恢复(显示计划并确认,详见 恢复操作
pig pb expireeexpire按保留策略清理过期备份(--plan 预览)
pig pb createcstanza-create创建 stanza
pig pb upgradeustanza-upgrade升级 stanza(版本升级或克隆善后)
pig pb deletedstanza-delete删除 stanza 及其全部备份
pig pb checkckcheck校验备份配置与归档链路
pig pb startupstart恢复 pgbackrest 操作
pig pb stopdwstop暂停 pgbackrest 操作
pig pb log [list/show/tail]l查看 pgbackrest 日志

启用备份

如果集群创建时 pgbackrest_enabledtrue(默认),备份已自动启用。 若创建时禁用了备份,或修改了仓库配置,可用 pg_backup 子任务补充配置:

./pgsql.yml -t pg_backup -l pg-meta   # 配置 pgbackrest,创建 stanza

集群初始化后 Pigsty 会自动尝试执行一次初始全量备份(标记文件 /etc/pgbackrest/initial.done);该任务会忽略失败,标记也不能证明备份成功,应以 pb info 为准。 定时备份计划通过 pg_crontab 声明,详见 备份策略


移除备份

移除主实例(pg_role = primary)时,Pigsty 默认一并删除该集群的备份 stanza:

./pgsql-rm.yml                          # 移除集群(含备份)
./pgsql-rm.yml -e pg_rm_backup=false    # 移除集群,但保留备份
./pgsql-rm.yml -t pg_backup             # 仅删除备份,保留集群

使用 pg_rm_backup(设为 false)保留备份,使用 pg_backup 子任务只删备份。 如果备份仓库为对象版本配置了 对象锁定保留期, 删除可能只写入 Delete Marker,被锁定的历史版本会继续占用空间,直到保留期结束。


手动备份

crontab 之外,随时可以手动触发备份。pg-backup 脚本与 pig pb backup 都带主库角色检查,在从库上执行会直接退出,不会产生错误的备份:

pg-backup            # 增量备份(仓库中无全量备份时自动升级为全量)
pg-backup full       # 全量备份
pg-backup diff       # 差异备份(相对最近一次全量)
pg-backup incr       # 增量备份(相对最近一次任意备份)

pig pb backup full   # 等价:pig 封装,自动检测 stanza 与 DBSU

备份期间会显著占用磁盘 I/O 与网络带宽(并行度已限制在 2~4 进程),建议安排在业务低峰执行。


查看备份

pb info 列出仓库中当前 stanza 的备份与 WAL 归档状态:

pb info          # = pgbackrest --stanza=pg-meta info
pig pb info      # 等价
pig pb list      # 列出仓库中所有 stanza

输出解读的关键是 备份标签20250715-013657F 为全量备份(F),..._20250715-013724D 为差异备份(D),..._20250715-013730I 为增量备份(I)—— 下划线前的部分标识备份链所依附的全量备份。wal archive min/max 显示 WAL 归档范围, 它与最早的全量备份共同描述 恢复窗口

监控系统同样提供备份状态的持续观测:pgbackrest_exporter(端口 9854)导出的 指标 覆盖最近备份时间、类型、大小与错误状态,可直接用于告警。


清理过期备份

保留策略默认在每次备份后自动执行(expire-auto)。手动触发或预览清理计划:

pig pb expire --plan    # 预览将被清理的备份(dry-run,不实际删除)
pig pb expire           # 按保留策略执行清理

Stanza 管理

Stanza 记录集群的备份身份(system-id 与主版本), 以下场景需要手动管理:

pig pb create    # 创建 stanza:新仓库初始化(Pigsty 集群初始化时自动执行)
pig pb upgrade   # 升级 stanza:PostgreSQL 大版本升级后,或克隆恢复出新集群后
pig pb delete    # 删除 stanza:连同其所有备份与归档一并删除,危险操作!

最常见的手动场景是 克隆集群的善后: 从其他集群的备份恢复出新集群后,stanza 中记录的 system-id 与新集群不符, 必须执行 stanza-upgrade 之后,新集群的备份才能写入仓库。


检查与启停

pig pb check     # 端到端校验:配置、归档推送、仓库可达性
pig pb stop      # 暂停 pgbackrest 操作(维护窗口,阻止新备份/归档启动)
pig pb start     # 恢复 pgbackrest 操作

check 命令会实际推送一个 WAL 段并确认其到达仓库,是排查"归档不工作"问题的第一步。


查看日志

pig pb log           # 列出日志文件
pig pb log tail      # 跟踪最新日志
ls /pg/log/pgbackrest/   # 日志目录:备份、归档、恢复各有独立日志文件

使用 pgsql-pitr.yml 时,PostgreSQL 的恢复日志位于 /pg/tmp/recovery.log


替代备份工具

pg-basebackup

Pigsty 另备有不依赖 pgbackrest 的独立备份脚本 /pg/bin/pg-basebackup, 它使用原生 pg_basebackup 生成单文件物理备份(lz4 压缩 tarball),默认写入 /pg/backup。 适合在不便使用备份仓库时快速留存一份物理副本:

pg-basebackup                        # 生成 /pg/backup/backup_<tag>_<date>.tar.lz4
pg-basebackup --dst /tmp --file backup.tar.lz4   # 指定输出位置与文件名

mkdir -p /tmp/data                   # 解压提取
cat /pg/backup/backup_pg-meta_20250713.tar.lz4 | unlz4 -d -c | tar -xC /tmp/data

逻辑备份

pg_dump 生成的逻辑备份 不能 用于 PITR,但它是跨大版本迁移、导出部分数据、长期归档快照的正确工具。 物理备份与逻辑备份互为补充,严肃的生产环境通常两者兼备。这是 PostgreSQL 自带的工具,请参阅 官方文档

5 - 恢复操作

执行时间点恢复:pgsql-pitr.yml 剧本、pig pitr 命令与 pig pb restore 原语,恢复目标、分步执行与完整参数参考。

Pigsty 提供三个层次的恢复入口,共用 同一套参数语义,按场景选用:

入口适用场景特点
pgsql-pitr.yml 剧本生产集群恢复编排整个集群:HA 暂停、多节点、etcd 清理、恢复控制信息输出
pig pitr 命令单节点集群 / 节点本机操作无需管理节点,在数据库节点上直接编排执行
pig pb restore 原语非 Patroni 托管的实例pgbackrest restore 的直接封装,最精细的控制

手把手的沙箱演练教程请参阅 手工恢复; 用恢复克隆出新集群(不影响生产的推荐姿势)请参阅 克隆数据库集群


快速上手

要将 pg-meta 集群回滚到之前的时间点,声明 pg_pitr 参数并运行剧本:

pg-meta:
  hosts: { 10.10.10.10: { pg_seq: 1, pg_role: primary } }
  vars:
    pg_cluster: pg-meta
    pg_pitr: { time: '2025-07-13 10:00:00+00', action: promote }   # 一步执行:到达目标后显式提升
./pgsql-pitr.yml -l pg-meta

参数也可以通过命令行临时传入,两种方式等价:

./pgsql-pitr.yml -l pg-meta -e '{"pg_pitr": { "time": "2025-07-13 10:00:00+00", "action": "promote" }}'

剧本会依次执行:暂停 Patroni 高可用 → 停止集群进程 → 执行 pgbackrest 增量还原 → 启动 PostgreSQL 并等待进入一致恢复状态 → 用 pg_controldata 打印控制信息 → 清理 etcd 元数据 → 重新拉起集群与高可用。 执行过程的第一步会打印完整的恢复计划(源集群、目标、还原命令),但不会暂停等待确认;上面的一步式示例因此显式声明了 action: promote。 如果需要在目标点检查数据,请使用下文的 分步执行,并显式选择 action: pause


恢复目标

pg_pitr 支持 六类恢复目标,其中四类目标值互斥,只能指定一个:

pg_pitr: { }  # 恢复到最新状态(WAL 归档流末尾)
pg_pitr: { time: "2025-07-13 10:00:00+00" }
pg_pitr: { lsn: "0/4001C80" }
pg_pitr: { xid: "250000" }
pg_pitr: { name: "some_restore_point" }
pg_pitr: { type: "immediate" }

未指定任何目标时,重放全部 WAL 归档恢复到最新状态(内部类型 default); immediate 类型在到达第一个一致点后立即停止,用于最快恢复出可用实例(例如验证备份)。

按时间恢复

最常用的目标。时间应为合法的 PostgreSQL TIMESTAMP 格式,建议带时区:YYYY-MM-DD HH:MM:SS+TZ

./pgsql-pitr.yml -l pg-meta -e '{"pg_pitr": { "time": "2025-07-13 10:00:00+00", "action": "promote" }}'

按名称恢复

在高危变更前用 pg_create_restore_point 打点,恢复时便有了无歧义的目标:

SELECT pg_create_restore_point('before_migration');
./pgsql-pitr.yml -l pg-meta -e '{"pg_pitr": { "name": "before_migration", "action": "promote" }}'

按事务 ID 恢复

如果误删数据的事务号已知(从监控仪表盘或 CSVLOG 的 TXID 字段获取), 配合 exclusive 精确停在该事务 之前,一条数据都不多丢:

./pgsql-pitr.yml -l pg-meta -e '{"pg_pitr": { "xid": "250000", "exclusive": true, "action": "promote" }}'

按 LSN 恢复

LSN(日志序列号)标识 WAL 流中的精确位置, 可从 Pigsty 仪表盘的 PG LSN 面板获取;需要时可以用 timeline 指定目标时间线(默认 latest):

./pgsql-pitr.yml -l pg-meta -e '{"pg_pitr": { "lsn": "0/4001C80", "timeline": "1", "action": "promote" }}'

恢复来源

默认从本集群自己的备份恢复,三个字段可以改变恢复来源:

  • cluster:源 stanza —— 使用共享仓库中 其他集群 的备份恢复(克隆集群 的基础)
  • repo:临时指定备份仓库定义(格式同 pgbackrest_repo 的仓库条目),例如从旧仓库或异地仓库恢复
  • set:从指定的 备份标签 开始还原(默认自动选择目标点前最近的备份集,用 pb info 查看可用标签)
./pgsql-pitr.yml -l pg-meta2 -e '{"pg_pitr": { "cluster": "pg-meta", "archive": false, "action": "promote" }}'           # 用 pg-meta 的备份恢复 pg-meta2
./pgsql-pitr.yml -l pg-meta2 -e '{"pg_pitr": { "cluster": "pg-meta", "time": "2025-07-14 08:00:00+00", "archive": false, "action": "promote" }}' # 并指定时间点

分步执行

一步到位固然方便,但在生产事故中,您可能希望亲手控制每个阶段。剧本的任务树支持用 tags 三步走:

./pgsql-pitr.yml -l pg-meta -t down     # 第一步:暂停 HA,停止 patroni 与 postgres
./pgsql-pitr.yml -l pg-meta -t pitr     # 第二步:执行还原与重放,打印恢复控制信息
./pgsql-pitr.yml -l pg-meta -t up       # 第三步:清理 etcd,拉起集群,恢复 HA
# down                 : # 停止高可用并关闭 patroni 和 postgres
#   - pause            : # 暂停 patroni 自动故障切换
#   - stop             : # 停止 patroni 和 postgres 服务
#     - stop_patroni   : # 停止 patroni 服务
#     - stop_postgres  : # 停止 postgres 服务
# pitr                 : # 执行 PITR 过程
#   - config           : # 生成 pgbackrest 配置和恢复脚本
#   - restore          : # 运行 pgbackrest 恢复命令
#   - recovery         : # 启动 postgres 并完成恢复
#   - verify           : # 验证恢复后的集群控制数据
# up:                  : # 启动 postgres / patroni 并恢复高可用
#   - etcd             : # 启动前清理 etcd 元数据
#   - start            : # 启动 patroni 和 postgres 服务
#     - start_postgres : # 启动 postgres 服务
#     - start_patroni  : # 启动 patroni 服务
#   - resume           : # 恢复 patroni 自动故障切换

每步之间您可以检查状态:down 之后确认进程已停;pitr 阶段返回后,先查看 /pg/tmp/recovery.log, 并用 pg_is_in_recovery()pg_is_wal_replay_paused()pg_last_wal_replay_lsn()pg_last_xact_replay_timestamp() 确认是否已经到达目标,再结合业务查询抽查数据。pg_controldata /pg/data 提供的是检查点与时间线摘要,不能单独证明时间、XID 或 LSN 目标已经命中。 使用 action: pause 时,确认无误后先提升实例,再执行 up;如果恢复目标选错了,可在 up 之前调整 pg_pitr 重跑 pitr 阶段。 pause / shutdown 需要配合这种分阶段流程才能形成明确的人工门;一步式执行应显式使用 action: promote

SELECT pg_is_in_recovery(), pg_is_wal_replay_paused(),
       pg_last_wal_replay_lsn(), pg_last_xact_replay_timestamp();
pg_ctl -D /pg/data promote              # action: pause 验证通过后执行
./pgsql-pitr.yml -l pg-meta -t up       # 清理 etcd,拉起 Patroni,恢复 HA

PITR 参数定义

pg_pitr 的完整字段如下。恢复目标、目标动作与原数据保留方式都建议显式声明:

pg_pitr:                         # 定义 PITR 恢复任务
  cluster: pg-meta               # 恢复来源集群(源 stanza),默认为本集群 pg_cluster
  type: default                  # 目标类型:default | time | xid | name | lsn | immediate
  time: "2025-07-13 10:00:00+00" # 恢复目标:时间点(与 xid / name / lsn 互斥)
  name: "some_restore_point"     # 恢复目标:命名恢复点(与 time / xid / lsn 互斥)
  xid: "250000"                  # 恢复目标:事务 ID(与 time / name / lsn 互斥)
  lsn: "0/4001C80"               # 恢复目标:日志序列号(与 time / xid / name 互斥)
  exclusive: false               # 排除目标点本身,默认包含(仅 time / xid / lsn 有效)
  timeline: latest               # 目标时间线,可为整数,默认 latest
  set: latest                    # 从哪个备份标签开始还原,默认自动选择
  action: pause                  # 到达目标后动作:pause / promote / shutdown
                                 # 指定恢复目标时未声明 action,实际默认 pause
  archive: true                  # 保留原归档配置;探索性恢复设为 false(archive-mode=off)
  backup: false                  # 恢复前将原数据目录搬到 /pg/data-backup;重跑会覆盖已有备份目录
  db_exclude: []                 # 排除的数据库(选择性恢复)
  db_include: []                 # 只恢复指定数据库(选择性恢复)
  link_map:                      # 目录软链接映射(表空间 / WAL 分盘)
    pg_wal: '/data/wal'
    pg_xact: '/data/pg_xact'
  process: 4                     # 恢复并行进程数,默认为节点 CPU 核数
  repo: {}                       # 临时指定恢复来源仓库(格式同 pgbackrest_repo 条目)
  data: /pg/data                 # 恢复到的数据目录
  port: 5432                     # 恢复实例的监听端口

每个字段与 pgbackrest 选项的对应关系,见 参数映射表


单实例:pig pitr

在数据库节点上,pig pitr 无需 Ansible 环境即可执行单节点恢复编排: 预检(校验目标、stanza 与备份存在性)→ 停止 Patroni 与 PostgreSQL → 执行还原 → 按参数决定是否启动 PostgreSQL → 恢复后指引。

pig pitr -t "2025-07-13 10:00:00+00"    # 恢复到时间点
pig pitr --xid 250000 -X                # 恢复到事务 250000 之前(-X = --exclusive)
pig pitr --name before_migration        # 恢复到命名还原点
pig pitr -d                             # 恢复到 WAL 归档末尾(--default)
pig pitr -I --no-restart                # 只还原并准备 immediate 恢复,PostgreSQL 保持停止
pig pitr -t "..." --plan                # 只显示执行计划,不实际执行

常用选项:-b/--set 指定备份集,-T/--target-timeline 指定时间线,--target-action 指定到达目标后的动作, -D/--data 恢复到其他数据目录(此时必须配合 --no-restart)。 默认只用安全的 fast 模式停库,失败即中止 —— 除非显式给出 --force-stop,才允许升级为强制停库。 对于 Patroni 托管的数据目录,命令恢复后会让 Patroni 保持停止;验证数据后再执行 pig pt startpig pitr 不清理 etcd、不重建副本,也不会自动把实例重新加入 HA 集群。 完整选项参阅 pig pitr 命令手册


原语:pig pb restore

对于 不由 Patroni 托管 的实例(或已明确停管的场景),可以使用最底层的恢复原语 —— 它是 pgbackrest restore 的直接封装,自动处理 stanza、DBSU 与时间格式, 执行前显示恢复计划并要求确认:

pig pb restore --time "2025-07-13 10:00"     # 时间可省略时区与秒,自动按本地时区规范化
pig pb restore --set 20250715-013657F        # 从指定备份集恢复
pig pb restore -d                            # 恢复到归档末尾

两道内置的安全边界值得了解:

  • Patroni 托管实例会被硬拒绝:若 Patroni 服务活跃且目标是其托管的数据目录,pig pb restore 直接报错退出 —— 因为 Patroni 会立刻把恢复到一半的实例重新拉起。托管实例请使用 pig pitrpgsql-pitr.yml
  • PostgreSQL 必须已停止:实例仍在运行时拒绝执行。

-- 之后可透传原生 pgbackrest 选项(如 --tablespace-map--link-all), 但恢复目标、stanza、仓库等关键选项已被封装接管,不允许透传覆盖。详见 pig pb 命令手册


恢复后处理

恢复完成后,剧本会打印控制信息并重建高可用,但仍有三件事需要确认:

  1. 核对数据:按 分步执行 中的方法检查恢复目标与业务数据。

  2. 重建备份:如果是从其他集群克隆恢复,需要执行 stanza 善后; 无论何种恢复,都建议尽快执行一次全量备份,在新时间线上重建恢复起点:

    pg-backup full
    
  3. 恢复归档:探索性恢复若设置了 archive: false,归档已被关闭,验证完成后必须恢复 (archive_mode 是 postmaster 参数,需要重启生效):

    psql -c 'ALTER SYSTEM RESET archive_mode;'
    pg restart pg-meta   # 重启集群使归档配置生效
    pg-backup full       # 执行新的全量备份
    

6 - 克隆数据库集群

用 PITR 把一个集群的历史状态恢复到另一个集群:找回误删数据、执行恢复演练,以及必不可少的 Stanza 善后。

克隆是恢复能力最有价值的用法:不动生产集群,把它的历史状态恢复到另一套集群上。 误删数据后从克隆库中导回、定期演练验证备份可用性、审计取证查看历史状态、把测试环境重置为生产某刻的快照 —— 这些场景的操作方式完全相同,本页给出完整流程。

目标集群需要能访问源集群的备份仓库、允许被覆盖,并使用兼容的 PostgreSQL 主版本。使用集中式仓库(MinIO / S3)时, 仓库中以 stanza 隔离的各集群备份,对持有相应凭据的目标集群可见。


克隆现有集群

假设四节点沙箱中有 pg-metapg-test 两套集群,共享 MinIO 备份仓库。 要把 pg-test 重置为 pg-meta最新状态,只需在 pg_pitr 中把恢复来源指向 pg-meta 的 stanza:

./pgsql-pitr.yml -l pg-test -e '{"pg_pitr": { "cluster": "pg-meta", "archive": false, "action": "promote" }}'

配合恢复目标,可以克隆到恢复窗口内的 任意时间点 —— 例如把 pg-test 重置为 pg-meta 在 2025 年 12 月 26 日 15:30 的状态:

./pgsql-pitr.yml -l pg-test -e '{"pg_pitr": { "cluster": "pg-meta", "time": "2025-12-26 15:30:00+08", "archive": false, "action": "promote" }}'

跨集群克隆显式设置 archive: false,在独立恢复阶段关闭归档;Patroni 接管后按下面的步骤处理目标 stanza 与归档。

目标集群也可以是全新创建的空集群:先用标准流程 创建集群(如 pg-meta2), 再对它执行跨集群 PITR,即完成了"从备份仓库引导新集群"。

pgBackRest 的还原是增量的(delta):只重写与备份不一致的文件。 因此对反复执行的演练、或已通过 备份集群(Standby Cluster) 物理复制拉齐过数据的目标集群,克隆速度会显著快于首次全量还原。

误删数据的典型找回流程到这里只剩最后一步:在克隆集群中验证数据无误后, 用 pg_dump 导出受影响的表/库,导回生产集群。全库原地回滚是最后手段,而不是第一反应。


克隆善后

克隆出的新集群带着 源集群的数据,但备份仓库中它自己的 stanza 仍记录着 原来的身份(system-id)。 pgBackRest 写入备份前会核对身份,不一致即拒绝 —— 这个保护机制防止新集群的备份污染源集群的备份历史。

因此,确认克隆结果符合预期后,必须执行三步善后,新集群的备份链路才能恢复正常:

pb stanza-upgrade                          # 1. 升级 stanza,接纳新集群的 system-id(仅跨集群克隆需要)
psql -c 'ALTER SYSTEM RESET archive_mode;' # 2. 恢复归档(本文跨集群示例显式设置了 archive: false)
pg restart pg-test                         #    archive_mode 为 postmaster 参数,重启生效
pg-backup full                             # 3. 全量备份,让新集群从此刻起拥有自己的备份历史

跳过善后的后果是可预期的:下一次例行备份会因身份核对失败而报错, 在此期间新集群 没有备份保护(如果关闭了归档,也不会产生新的 WAL 归档):

postgres@pg-test-1:~$ pb backup
INFO: backup command begin 2.57.0: --annotation=pg_cluster=pg-test ... --stanza=pg-test --start-fast
ERROR: [051]: PostgreSQL version 18, system-id 7588470953413201282 do not match stanza version 18, system-id 7588470974940466058
       HINT: is this the correct stanza?
INFO: backup command end: aborted with exception [051]

重建备份身份

stanza-upgrade 让新集群沿用原 stanza 继续写备份。如果您希望新集群拥有 全新的备份历史 (例如克隆出的集群将长期独立演化),也可以选择彻底重建其备份配置 —— 声明式方式:

./pgsql-rm.yml -t pg_backup -l pg-test -e pg_rm_backup=true   # 删除 pg-test 的 stanza 与旧备份
./pgsql.yml    -t pg_backup -l pg-test                        # 重新配置 pgbackrest,创建全新 stanza
pg-backup full                                                # 建立第一个恢复点

或者用 pgbackrest 原语 手动完成同样的事情:

pb stop --force            # 暂停 pgbackrest 操作
pb stanza-delete --force   # 删除 stanza 及其备份(危险操作,确认无误再执行)
pb start                   # 恢复 pgbackrest 操作
pb stanza-create           # 以当前集群身份重建 stanza
pg-backup full             # 建立第一个恢复点

在线副本:备份集群

克隆得到的是 静态的时间点快照。如果需要的是持续跟随源集群的 在线副本, 应使用 备份集群(Standby Cluster,基于流复制); 需要"一直落后一小时"的快速反悔窗口,则使用 延迟集群

三者互为补充:备份集群提供实时副本,延迟集群提供固定延迟的反悔窗口, PITR 克隆提供恢复窗口内的历史快照 —— 且不需要提前准备在线副本。


恢复演练

克隆是 不触碰生产集群的恢复演练:目标集群会被覆盖,但它能端到端验证备份系统的每个环节。 建议将以下演练纳入例行运维(每季度,或每次重大变更后):

  1. 选定演练目标:生产集群恢复窗口内的某个时间点;
  2. 向演练集群执行跨集群 PITR,记录耗时 —— 这就是实测的 PITR RTO;
  3. 验证数据完整性:行数抽查、关键业务表校验、应用连通测试;
  4. 执行 克隆善后,确认演练集群自身备份恢复正常(验证善后流程本身也是演练的一部分);
  5. 记录结果:恢复耗时、发现的问题、文档与实际操作的出入。

沙箱环境中使用 pgbackrest 原语手工执行恢复的完整教程,参阅 手工恢复; 在同一台机器上用 XFS 快照快速 Fork 实例的进阶技巧,参阅 Fork 实例