时间点恢复的实现架构

Pigsty 以 pgBackRest 为引擎实现 PITR:仓库抽象、归档链路、调度机制,以及"备份跟随主库"的工程设计。

原理 一页就能讲完,工程却没有那么简单: 归档不能拖垮主库的写入性能,备份放到对象存储上要加密,主从切换之后备份不能中断, 多套集群共用一个仓库时要相互隔离,海量小文件会拖垮备份吞吐……

Pigsty 选择 pgBackRest 作为备份引擎,并把这些工程问题的答案预置在了出厂配置中。 本文说明这套架构的组成:引擎、仓库、链路、调度,以及一个关键设计 —— 备份跟随主库。


备份引擎:pgBackRest

pgBackRest 是 PostgreSQL 生态中事实上的标准备份工具,Pigsty 用它承担三项职责: 执行基础备份(backup)、接收 WAL 归档(archive-push)、执行恢复(restore / archive-get)。 选择它的理由,恰好对应上面那些工程问题:

  • 并行:备份、归档、恢复都支持多进程并行,吞吐可以随核数扩展。
  • 增量:支持差异/增量备份与 块级增量(block incremental),只传输文件内部变化的块。
  • 压缩与加密:内置 zstd 压缩与 AES-256-CBC 加密,密文落盘,仓库泄露不等于数据泄露。
  • 多种仓库:本地磁盘、S3 兼容对象存储(MinIO、云厂商 OSS)、Azure、GCS、SFTP 皆可作为后端。
  • 打包bundle 特性将海量小文件合并为大对象存储,避免对象存储的小文件惩罚。

在仓库内部,pgBackRest 使用 stanza(节)隔离不同集群的备份。Pigsty 将 stanza 直接映射为集群名 pg_cluster,因此多套集群可以安全地共享同一个备份仓库:

备份仓库
├── backup/
│   ├── pg-meta/          # pg-meta 集群的基础备份
│   └── pg-test/          # pg-test 集群的基础备份
└── archive/
    ├── pg-meta/          # pg-meta 集群的 WAL 归档
    └── pg-test/          # pg-test 集群的 WAL 归档

仓库抽象

备份放在哪里,是备份策略中最重要的决定。Pigsty 把这个决定抽象为两个参数: pgbackrest_method 选择使用哪个仓库, pgbackrest_repo 定义所有候选仓库。默认提供两个开箱即用的选项:

pgbackrest_method: local          # 使用哪个仓库:local,minio,或自定义仓库名
pgbackrest_repo:                  # 仓库定义:https://pgbackrest.org/configuration.html#section-repository
  local:                          # 默认仓库:主库本地文件系统
    path: /pg/backup              # 备份目录,默认挂在主数据盘上
    retention_full_type: count    # 按份数保留全量备份
    retention_full: 2             # 保留最近 2 个全量备份(清理前最多 3 个)
  minio:                          # 可选仓库:MinIO / S3 兼容对象存储
    type: s3                      # MinIO 走 S3 兼容协议
    s3_endpoint: sss.pigsty       # MinIO 服务端点(负载均衡域名)
    s3_bucket: pgsql              # 桶名称
    s3_key: pgbackrest            # 访问密钥
    s3_key_secret: S3User.Backup  # MinIO 用户密钥
    storage_ca_file: /etc/pki/ca.crt  # 使用 Pigsty 自签名 CA 验证 HTTPS
    block: y                      # 启用块级增量备份
    bundle: y                     # 小文件打包存储
    cipher_type: aes-256-cbc      # 仓库加密:AES-256-CBC
    cipher_pass: pgBackRest       # 仓库加密密码
    retention_full_type: time     # 按时间保留全量备份
    retention_full: 14            # 按时间保留 14 天

注意两套预置仓库的策略差异:本地仓库 追求简单直接 —— 不加密、不打包、按份数保留; MinIO 仓库 面向生产 —— 加密、打包、块级增量、按时间保留两周。 这不是随意的默认值,而是对两种使用场景的判断:本地仓库与数据同生共死,重点是快; 远程仓库承担容灾职责,重点是安全与可追溯。

仓库定义到 pgBackRest 配置的转换是机械的:键名中的下划线替换为连字符,加上 repo1- 前缀, 渲染进 /etc/pgbackrest/pgbackrest.conf。所以 pgBackRest 支持的任何仓库选项都可以直接写进 pgbackrest_repo —— 例如添加一个云上 S3 仓库用于异地冷备:

s3:    # 自定义仓库 ------> /etc/pgbackrest/pgbackrest.conf
  type: s3                        # ----> repo1-type=s3
  s3_endpoint: oss-cn-beijing-internal.aliyuncs.com
  s3_region: oss-cn-beijing       # ----> repo1-s3-region=oss-cn-beijing
  s3_bucket: <your_bucket>        # ----> repo1-s3-bucket=<your_bucket>
  s3_key: <your_access_key>       # ----> repo1-s3-key=<your_access_key>
  s3_key_secret: <your_secret>    # ----> repo1-s3-key-secret=<your_secret>
  s3_uri_style: host              # ----> repo1-s3-uri-style=host
  path: /pgbackrest               # ----> repo1-path=/pgbackrest
  cipher_type: aes-256-cbc        # ----> repo1-cipher-type=aes-256-cbc
  cipher_pass: <your_password>    # ----> repo1-cipher-pass=<your_password>
  retention_full_type: time       # ----> repo1-retention-full-type=time
  retention_full: 90              # ----> repo1-retention-full=90

各类仓库的完整配置方法(MinIO、阿里云 OSS、AWS S3、版本控制与对象锁定)请参阅 备份仓库


归档与调度

WAL 归档链路在集群初始化时自动接通:只要 pgbackrest_enabled 为真(默认),Patroni 配置模板就会为集群设置 archive_mode: onarchive_command: pgbackrest --stanza=<集群名> archive-push %p,WAL 段从此源源不断地流入备份仓库。

基础备份的生产则有两个入口:

  • 初始备份:集群初始化完成后,Pigsty 默认在主库上尝试执行一次全量备份(留下 /etc/pgbackrest/initial.done 标记,避免重复)。 可通过 pgbackrest_init_backup 关闭。
  • 定时备份pg_crontab 参数声明备份计划,写入 postgres 用户的 crontab。 Pigsty 随附的标准集群配置声明每天凌晨一点的全量备份;角色参数本身的默认值为空列表:
pg_crontab: [ '00 01 * * * /pg/bin/pg-backup full' ]

pg-backup 是 pgBackRest 的薄封装:自动解析 stanza,执行 pgbackrest backup,并做一件重要的事 —— 角色检查


备份跟随主库

pgBackRest 安装在集群的 所有 节点上,但任何时刻只有 当前主库 实际执行备份与归档: pg-backup 在运行前检查节点角色,从库上直接退出。这个看似简单的设计带来一个重要性质 —— 备份链路与高可用拓扑解耦

  • 所有节点的备份配置完全相同,crontab 也完全相同;
  • 故障切换 后,新主库自动接续后续备份与 WAL 归档,无需人工干预;
  • 备份仓库只有一份由当前主库写入的权威数据流,不存在双写冲突。

仓库在另一个方向上也参与高可用:当使用远程仓库时,Pigsty 将 pgBackRest 注册为 Patroni 的备用副本创建方式 (create_replica_methods)。默认仍先尝试 basebackup,失败后才使用 pgbackrest --delta restore 从仓库拉取数据; 走到这一后备路径时,造从库的流量压力会从主库转移到备份仓库。


性能取舍

出厂配置中还有几处针对性能的预置判断,体现同一个原则:备份为生产让路,恢复全力以赴

配置默认值考量
压缩算法zstd高压缩比与高吞吐的平衡点,备份体积通常远小于原库
备份/归档并行度约 1/4 核数(2~4 进程)备份不与生产负载争抢 CPU
恢复并行度核数(至多 8 进程)恢复时争分夺秒,资源全开
异步归档archive-async=y/pg/spool 假脱机批量推送,归档不阻塞写入
归档队列上限4 GiB未归档 WAL 积压超限时丢弃归档,保护主库磁盘不被写满
快速启动start-fast=y备份开始时立即执行检查点,不等常规检查点周期
增量恢复delta=y恢复时复用数据目录中未变化的文件,大幅缩短 RTO

归档队列上限的具体故障行为见 工作原理


可观测性

备份不被观测,就等于没有备份。每个 PostgreSQL 节点默认运行 pgbackrest_exporter(端口 9854), 将仓库中的备份状态导出为监控指标:最近一次备份的时刻、类型、大小、持续时间、错误状态 —— Grafana 监控面板与告警规则开箱即用。此外还有几个便捷入口:

入口说明
pb infopgbackrest info 的别名封装,查看仓库中的备份列表
/pg/log/pgbackrest/备份、归档、恢复的详细日志
pg-backup手动触发备份:full / diff / incr

关于备份的日常管理命令,请参阅 管理命令; 理解了架构之后,下一个问题是如何为您的场景选择策略 —— 请继续阅读 策略权衡