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

返回本页常规视图.

备份恢复

基于 pgBackRest 的时间点恢复能力,应对软件缺陷与人为误删。

如果您只有一分钟,请记住这张图:

flowchart TB
    subgraph Timeline["时间点恢复 = 数据库时光机"]
        direction LR
        Mon["📅 周一凌晨<br/>全量备份"] --> |WAL归档| Tue["📅 周二<br/>增量备份"]
        Tue --> |WAL归档| Wed["📅 周三<br/>增量备份"]
        Wed --> |WAL归档| Thu["💥 周四 10:30<br/>灾难 DROP!"]
    end

    Thu --> |pg-pitr --time=10:29:59| Recovery

    subgraph Recovery["🔄 恢复流程"]
        R1["1️⃣ 下载周一全量备份"]
        R2["2️⃣ 应用周二、周三增量备份"]
        R3["3️⃣ 重放 WAL 到 10:29:59"]
        R4["4️⃣ 数据恢复到灾难前一秒!"]
        R1 --> R2 --> R3 --> R4
    end

    Mon -.-> |备份仓库| R1

核心价值DROP TABLE 了?别慌。只要有备份,就能把数据库恢复到"删表前一秒"的状态。


本章内容

章节 说明 核心问题
PITR 原理 基础备份 + WAL 归档如何实现时间点恢复 时光机是怎么工作的?
备份策略 本地备份、MinIO、S3 等不同备份仓库的配置 备份放哪里?保留多久?
恢复机制 恢复命令、恢复场景与操作步骤 出事了怎么恢复?

为什么需要 PITR?

问题:高可用无法应对的场景

高可用 能应对硬件故障,但对以下场景无能为力

flowchart LR
    subgraph Problem["❌ 高可用无法解决的问题"]
        direction TB
        S1["场景一:误删数据<br/>DROP TABLE users"]
        S2["场景二:错误更新<br/>UPDATE orders SET..."]
        S3["场景三:应用 Bug<br/>数据污染"]
        S4["场景四:勒索软件<br/>数据被加密"]
    end

    S1 --> |复制| R1["从库也删了!"]
    S2 --> |复制| R2["所有订单取消!"]
    S3 --> |复制| R3["污染扩散!"]
    S4 --> |复制| R4["全部加密!"]

    R1 & R2 & R3 & R4 --> Conclusion["💀 主从切换毫无帮助<br/>所有节点数据一样糟糕"]

解决方案:时间点恢复

PITR 让您拥有"时光机",可以将数据库恢复到过去任意时刻

flowchart TB
    Disaster["💥 周四 10:30 - 误删表"] --> HA["❌ 高可用:无能为力<br/>所有节点都删了"]
    Disaster --> PITR["✅ PITR:恢复到 10:29:59"]

    subgraph Repo["📦 备份仓库"]
        Full["全量备份"]
        WAL1["WAL 归档"]
        WAL2["WAL 归档"]
    end

    PITR --> Repo
    Repo --> Recovered["🎉 恢复后的数据库<br/>表还在!数据完整!"]

高可用 vs PITR

两者是互补关系,不是替代关系:

特性 高可用 PITR
解决的问题 硬件故障、进程崩溃 数据误删、软件缺陷
RTO < 30 秒 分钟到小时级
RPO < 1MB 取决于 WAL 归档间隔
数据冗余位置 从库本地磁盘 备份仓库(可远程)
恢复方式 自动切换 手动恢复
典型场景 磁盘损坏、服务器宕机 DROP TABLE、数据污染

最佳实践:同时启用高可用和 PITR。

  • 高可用:应对日常硬件故障,自动恢复
  • PITR:应对逻辑错误,提供最后防线

工作原理

PITR 基于两个核心组件协同工作:

基础备份

数据库文件的完整快照,定期执行(如每天一次):

flowchart LR
    subgraph Source["数据库数据目录"]
        S1["base/"]
        S2["pg_wal/"]
        S3["pg_xact/"]
        S4["..."]
    end

    Source --> |全量拷贝| Repo

    subgraph Repo["备份仓库"]
        B1["📦 全量备份<br/>2024-01-15<br/>100GB"]
    end

    subgraph Modes["三种备份模式"]
        M1["🔵 full:完整拷贝<br/>恢复最简单"]
        M2["🟡 diff:自上次全量变更<br/>平衡性能"]
        M3["🟢 incr:自上次备份变更<br/>最省空间"]
    end

WAL 归档

记录所有数据变更的日志,持续归档:

flowchart LR
    subgraph PG["PostgreSQL"]
        W1["每个事务"]
        W2["先写 WAL"]
        W3["再写数据"]
        W1 --> W2 --> W3
    end

    PG --> |持续归档| Repo

    subgraph Repo["备份仓库"]
        WAL1["000000010000000000000001"]
        WAL2["000000010000000000000002"]
        WAL3["000000010000000000000003"]
        WAL4["..."]
    end

    subgraph Note["📝 WAL = 操作日志"]
        N1["记录每一个 INSERT/UPDATE/DELETE"]
        N2["重放 WAL 可重现数据变更"]
    end

恢复流程

基础备份 + WAL 归档 = 任意时间点的数据状态

flowchart TB
    subgraph Step1["Step 1: 恢复基础备份"]
        Full["📦 全量备份<br/>(周一)"] --> |拷贝| DataDir["📂 数据目录<br/>/pg/data"]
    end

    subgraph Step2["Step 2: 重放 WAL 到目标时间点"]
        WAL["📜 WAL 归档<br/>(周一-周四)"] --> |重放| Recovery["🔄 恢复进程<br/>恢复到周四 10:29:59"]
    end

    DataDir --> Recovery
    Recovery --> Result["✅ 数据库状态<br/>= 周四 10:29:59"]

默认配置

Pigsty 开箱即用,默认启用本地备份:

pgbackrest_enabled: true      # 启用 pgBackRest
pgbackrest_method: local      # 使用本地备份仓库
pgbackrest_repo:
  local:
    path: /pg/backup          # 备份存储路径
    retention_full_type: count
    retention_full: 2         # 保留 2 个全量备份

默认效果:

配置项 默认值 说明
备份位置 /pg/backup(主库本地) 可配置为 MinIO/S3
保留策略 最近 2 个全量备份 可按时间或数量保留
WAL 归档 自动启用 持续归档到备份仓库
RPO 约 16MB 最多丢失一个 WAL 段

备份仓库选择

根据可靠性需求选择合适的备份仓库:

仓库类型 优点 缺点 适用场景
本地磁盘 配置简单、恢复快 主库故障可能丢失 开发测试
MinIO 独立存储、加密支持 需要部署 MinIO 生产环境
S3/GCS/Azure 高可靠、跨区域 有流量费用 云上部署

生产推荐配置

# 使用 MinIO 作为备份仓库
pgbackrest_method: minio
pgbackrest_repo:
  minio:
    type: s3
    s3_endpoint: sss.pigsty
    s3_bucket: pgsql
    s3_key: pgbackrest
    s3_key_secret: S3User.Backup
    cipher_type: aes-256-cbc      # 启用加密
    cipher_pass: YourSecurePass   # 加密密码
    retention_full_type: time
    retention_full: 14            # 保留 14 天

RPO 与 RTO

RPO(可能丢失的数据量)

flowchart LR
    WAL1["📦 WAL 段 1<br/>16 MB 已归档"] --> WAL2["📦 WAL 段 2<br/>16 MB 已归档"]
    WAL2 --> WAL3["📦 WAL 段 3<br/>16 MB 已归档"]
    WAL3 --> Current["⚠️ 当前写入<br/>8 MB 未归档"]
    Current --> Failure["💥 故障发生"]

    Failure --> RPO["📊 RPO = 8 MB<br/>最后未归档的数据"]
    RPO --> Tip["💡 可通过 archive_timeout<br/>参数强制更频繁归档"]

RTO(恢复所需时间)

恢复时间取决于:

  1. 下载基础备份:与数据量和网络带宽相关
  2. 重放 WAL:与恢复的时间跨度相关
数据量 备份位置 估计 RTO
10 GB 本地 SSD 2-5 分钟
100 GB 本地 SSD 15-30 分钟
1 TB 本地 SSD 2-4 小时
100 GB 远程 MinIO 30-60 分钟

快速操作

查看备份状态

# 在主库节点执行
pgbackrest --stanza=pg-meta info

执行备份

# 全量备份
/pg/bin/pg-backup full

# 差异备份(推荐日常使用)
/pg/bin/pg-backup diff

# 增量备份
/pg/bin/pg-backup incr

恢复到指定时间点

# 停止 PostgreSQL
sudo systemctl stop patroni

# 恢复到具体时间
pg-pitr --time="2024-01-15 10:29:00"

# 启动 PostgreSQL
sudo systemctl start patroni

常见恢复场景

场景 命令 说明
误删表,恢复到删除前 pg-pitr --time="10:29:00" 指定删除前的时间
磁盘损坏,恢复全部数据 pg-pitr --type=immediate 恢复到最新状态
恢复到命名还原点 pg-pitr --name="before_migration" 需要提前创建还原点
恢复到指定事务 pg-pitr --xid=12345678 需要知道事务 ID

接下来

深入了解备份恢复的细节:

  • 📚 PITR 原理:理解基础备份与 WAL 归档的工作机制
  • 💾 备份策略:配置本地备份、MinIO、S3 等仓库
  • 🔄 恢复机制:详细的恢复命令和操作步骤

相关话题:

1 - PITR 原理

基础备份与 WAL 归档如何协同工作,实现时间点恢复。

时间点恢复(Point-In-Time Recovery,PITR)是 PostgreSQL 的核心能力之一。它允许将数据库恢复到过去任意时刻的状态。


核心概念

基础备份(Base Backup)

基础备份是数据库数据文件的完整拷贝,包括:

  • 所有表空间的数据文件
  • 配置文件
  • 事务日志的起始位置

pgBackRest 支持三种备份类型:

类型 说明 适用场景
全量备份(full) 完整拷贝所有数据 首次备份、周期性完整备份
差异备份(diff) 自上次全量以来的变更 日常备份,恢复速度快
增量备份(incr) 自上次任意备份以来的变更 频繁备份,节省空间

WAL 归档

WAL(Write-Ahead Log)记录了数据库的所有变更操作:

  • 每个事务提交前,变更先写入 WAL
  • WAL 文件按段组织,默认每段 16MB
  • 段写满后自动归档到备份仓库
数据变更 → 写入 WAL → WAL 段写满 → 归档到备份仓库

恢复原理

PITR 恢复分为两个阶段:

阶段一:恢复基础备份

备份仓库                         目标服务器
┌──────────────┐               ┌──────────────┐
│ 基础备份      │ ───拷贝──→   │ 数据目录      │
│ (2024-01-15) │               │ /pg/data     │
└──────────────┘               └──────────────┘

从备份仓库下载并恢复基础备份到目标数据目录。

阶段二:重放 WAL

备份仓库                         目标服务器
┌──────────────┐               ┌──────────────┐
│ WAL 归档      │               │ PostgreSQL   │
│ 00000001...  │ ───重放──→   │ 恢复进程     │
│ 00000001...  │               │              │
│ 00000001...  │               │ 停止于目标时间 │
└──────────────┘               └──────────────┘

PostgreSQL 恢复进程从归档中读取 WAL,按顺序重放变更,直到:

  • 达到指定的目标时间点
  • 达到指定的事务 ID
  • 达到指定的 LSN 位置
  • 重放完所有可用的 WAL

恢复目标

pgBackRest 支持多种恢复目标类型:

目标类型 参数 说明
时间 --target-time 恢复到指定时间点
事务 ID --target-xid 恢复到指定事务
LSN --target-lsn 恢复到指定日志位置
命名点 --target-name 恢复到命名的恢复点
立即 --target-action=promote 恢复到最新可用状态

恢复时间线

每次 PITR 恢复都会创建新的时间线(Timeline):

Timeline 1: ─────────────────────────────────────────────→
                              │
                         故障点(DROP TABLE)
                              │
                              ↓ PITR 恢复
Timeline 2:                   ├─────────────────────────→
                              │
                         恢复到故障前

时间线机制确保:

  • 恢复后的数据不会与原时间线混淆
  • 可以对同一基础备份进行多次恢复
  • 保留完整的历史记录

关键参数

pgBackRest 相关

参数 默认值 说明
pgbackrest_enabled true 是否启用备份
pgbackrest_method local 备份仓库类型
pgbackrest_repo - 仓库配置详情

PostgreSQL 归档相关

参数 默认值 说明
archive_mode on 启用 WAL 归档
archive_command pgbackrest 归档命令
archive_timeout 300 强制归档超时(秒)

备份链

pgBackRest 使用备份链管理多个备份之间的依赖关系:

全量备份1 ──→ 差异1 ──→ 增量1 ──→ 增量2
    │
    └────→ 差异2 ──→ 增量3

全量备份2 ──→ 差异3

恢复时需要:

  • 一个全量备份
  • 加上依赖的差异/增量备份(如有)
  • 加上对应时间段的 WAL 归档

与流复制的区别

特性 流复制(高可用) PITR(备份恢复)
目的 实时热备,故障切换 历史数据恢复
数据延迟 毫秒级 可恢复到任意历史时间
存储位置 从库本地磁盘 备份仓库(本地/远程)
恢复速度 即时切换 需要恢复过程
防护范围 硬件故障 软件缺陷、人为误操作

最佳实践:两者结合使用,互为补充。

2 - 备份策略

本地备份、MinIO、S3 等不同备份仓库的配置与选择。

Pigsty 支持多种备份仓库类型,可根据可靠性需求、成本预算和恢复时间要求选择合适的策略。


仓库类型

本地仓库(Local)

备份存储在主库节点的本地磁盘上。

pgbackrest_method: local
pgbackrest_repo:
  local:
    path: /pg/backup
    retention_full_type: count
    retention_full: 2

优点

  • 配置简单,无需额外基础设施
  • 恢复速度快(本地 IO)
  • 无网络依赖

缺点

  • 主库节点故障时备份可能丢失
  • 占用主库磁盘空间
  • 不支持异地容灾

适用场景:开发测试、单节点部署、对 RPO 要求不高的场景

MinIO 仓库

备份存储在 MinIO 对象存储集群上。

pgbackrest_method: minio
pgbackrest_repo:
  minio:
    type: s3
    s3_endpoint: sss.pigsty
    s3_bucket: pgsql
    s3_key: pgbackrest
    s3_key_secret: S3User.Backup
    s3_uri_style: path
    retention_full_type: time
    retention_full: 14

优点

  • 备份与数据库物理隔离
  • 支持多副本,高可靠性
  • 支持加密存储
  • 可扩展存储容量

缺点

  • 需要部署 MinIO 集群
  • 恢复速度受网络带宽限制

适用场景:生产环境、需要异地容灾的场景

云对象存储(S3/GCS/Azure)

备份存储在云厂商的对象存储服务上。

pgbackrest_method: s3
pgbackrest_repo:
  s3:
    type: s3
    s3_endpoint: s3.amazonaws.com
    s3_bucket: my-pg-backup
    s3_key: AKIAXXXXXXXX
    s3_key_secret: xxxxxxxxxxxx
    s3_region: us-east-1
    retention_full_type: time
    retention_full: 30

优点

  • 无需自建存储基础设施
  • 跨地域复制,高可靠性
  • 按使用量付费

缺点

  • 产生存储和流量费用
  • 依赖互联网连接
  • 恢复时可能产生较高流量费

适用场景:云上部署、需要异地容灾但不想自建存储


保留策略

按数量保留

保留最近 N 个全量备份:

retention_full_type: count
retention_full: 2  # 保留 2 个全量备份

按时间保留

保留最近 N 天内的备份:

retention_full_type: time
retention_full: 14  # 保留 14 天内的备份

差异/增量保留

retention_diff: 7   # 保留 7 个差异备份
retention_incr: 14  # 保留 14 个增量备份

备份调度

默认调度

Pigsty 默认不设置自动备份调度,需要手动配置。

配置定时备份

通过 node_crontab 配置定时任务:

node_crontab:
  # 每天凌晨 1 点执行全量备份
  - '00 01 * * * postgres /pg/bin/pg-backup full'

  # 每 4 小时执行差异备份
  - '00 */4 * * * postgres /pg/bin/pg-backup diff'

推荐策略

数据量 全量备份 差异备份 说明
< 100GB 每天 1 次 - 小库直接全量
100GB ~ 1TB 每周 1 次 每天 1 次 平衡恢复时间和空间
> 1TB 每周 1 次 每天 1 次 + 增量 大库需要增量备份

加密配置

备份加密

对于远程仓库,强烈建议启用加密:

pgbackrest_repo:
  minio:
    cipher_type: aes-256-cbc
    cipher_pass: YourSecurePassword123!

支持的加密算法

  • aes-256-cbc:AES 256 位加密(推荐)
  • none:不加密(仅用于测试)

注意:务必安全保管加密密码,丢失后无法恢复备份!


压缩配置

pgBackRest 支持多种压缩算法:

pgbackrest_repo:
  local:
    compress_type: zst    # 压缩算法
    compress_level: 3     # 压缩级别 (1-9)
算法 特点
zst(默认) 高压缩率,快速,推荐使用
lz4 极快速,压缩率一般
gz 传统 gzip,兼容性好
bz2 高压缩率,速度较慢

块级增量备份

pgBackRest 2.x 支持块级增量备份,显著减少备份大小:

pgbackrest_repo:
  minio:
    block: y     # 启用块级备份
    bundle: y    # 小文件合并

优势

  • 只备份实际变更的数据块
  • 大幅减少备份大小和传输量
  • 特别适合大型数据库

多仓库配置

可以同时配置多个备份仓库:

pgbackrest_repo:
  local:
    path: /pg/backup
    retention_full: 2
  minio:
    type: s3
    s3_endpoint: sss.pigsty
    s3_bucket: pgsql
    retention_full: 14

使用场景

  • 本地仓库:快速恢复
  • 远程仓库:异地容灾

3 - 恢复机制

恢复命令、恢复场景与操作步骤详解。

本文介绍如何使用 Pigsty 的备份进行时间点恢复,包括常见恢复场景和操作步骤。


恢复工具

Pigsty 提供了封装好的恢复脚本:

/pg/bin/pg-pitr    # PITR 恢复脚本

也可以直接使用 pgBackRest 命令:

pgbackrest --stanza=<cluster> restore [options]

恢复场景

场景一:恢复到指定时间点

情况:用户在 10:30 误删了一张表,需要恢复到 10:29 的状态。

# 停止 PostgreSQL
sudo systemctl stop patroni

# 恢复到指定时间点
pg-pitr --time="2024-01-15 10:29:00"

# 启动 PostgreSQL
sudo systemctl start patroni

场景二:恢复到最新状态

情况:主库磁盘损坏,需要从备份恢复全部数据。

# 停止 PostgreSQL
sudo systemctl stop patroni

# 恢复到最新可用状态
pg-pitr --type=immediate

# 启动 PostgreSQL
sudo systemctl start patroni

场景三:恢复到新实例

情况:不影响现有服务,将备份恢复到一个新的实例进行验证。

# 在新节点上执行恢复
pgbackrest --stanza=pg-meta restore \
  --target-time="2024-01-15 10:29:00" \
  --pg1-path=/pg/data \
  --target-action=promote

# 启动新实例
pg_ctl start -D /pg/data

恢复参数

时间目标

# 恢复到指定时间点
pg-pitr --time="2024-01-15 10:29:00"

# 使用 ISO 8601 格式
pg-pitr --time="2024-01-15T10:29:00+08:00"

事务 ID 目标

# 恢复到指定事务 ID
pg-pitr --xid=12345678

LSN 目标

# 恢复到指定 LSN
pg-pitr --lsn="0/12345678"

命名恢复点

如果提前创建了恢复点:

-- 在数据库中创建恢复点
SELECT pg_create_restore_point('before_migration');

可以恢复到该点:

pg-pitr --name="before_migration"

恢复流程

标准恢复流程

1. 评估情况
   ↓
2. 确定恢复目标(时间/事务/LSN)
   ↓
3. 停止 PostgreSQL 服务
   ↓
4. 备份当前数据目录(可选)
   ↓
5. 执行恢复命令
   ↓
6. 验证恢复结果
   ↓
7. 启动 PostgreSQL 服务
   ↓
8. 重新配置复制关系(如需要)

详细步骤

第一步:停止服务

# 停止 Patroni(会自动停止 PostgreSQL)
sudo systemctl stop patroni

# 确认已停止
pg_isready -h /tmp

第二步:备份当前数据(可选)

# 如果需要保留当前状态
mv /pg/data /pg/data.bak.$(date +%Y%m%d%H%M%S)

第三步:执行恢复

# 使用 Pigsty 封装脚本
pg-pitr --time="2024-01-15 10:29:00"

# 或使用 pgBackRest 原生命令
pgbackrest --stanza=pg-meta restore \
  --target-time="2024-01-15 10:29:00" \
  --target-action=promote \
  --delta

第四步:启动服务

# 启动 Patroni
sudo systemctl start patroni

# 检查状态
patronictl -c /pg/bin/patroni.yml list

第五步:验证恢复

# 检查数据是否正确恢复
psql -c "SELECT * FROM recovered_table LIMIT 10;"

恢复选项

–delta

增量恢复,只替换有变化的文件:

pg-pitr --time="..." --delta

优势:恢复速度更快,特别是数据变化不大时。

–target-action

恢复完成后的动作:

选项 说明
promote(默认) 提升为可写状态
pause 暂停在恢复完成点
shutdown 关闭数据库

–type

恢复类型:

选项 说明
time 恢复到指定时间
xid 恢复到指定事务 ID
lsn 恢复到指定 LSN
name 恢复到命名恢复点
immediate 恢复到最新可用状态

注意事项

时间线变更

PITR 恢复会创建新的时间线,恢复后:

  • 时间线 ID 增加
  • 无法直接与原时间线的从库同步
  • 需要重新初始化从库

从库处理

恢复主库后,从库需要:

# 方式一:重新初始化从库
patronictl -c /pg/bin/patroni.yml reinit <cluster> <replica>

# 方式二:从备份重建从库
pg-pitr --type=immediate --delta  # 在从库节点执行

验证备份

定期验证备份可恢复性:

# 检查备份完整性
pgbackrest --stanza=pg-meta check

# 验证备份信息
pgbackrest --stanza=pg-meta info

常见问题

Q:恢复需要多长时间?

取决于:

  • 数据库大小
  • 备份仓库位置(本地/远程)
  • 需要重放的 WAL 数量
  • 磁盘/网络 IO 性能

经验值:100GB 数据库,本地备份,恢复约 15-30 分钟。

Q:可以恢复到任意时间点吗?

只能恢复到 WAL 归档覆盖的时间范围内。如果 WAL 已被清理,则无法恢复到那之前的时间点。

Q:恢复会影响现有服务吗?

在原实例上恢复会导致服务中断。如需不中断服务,可以恢复到新实例。