如果您只有一分钟,请记住这张图:
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(恢复所需时间)
恢复时间取决于:
- 下载基础备份:与数据量和网络带宽相关
- 重放 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 互补的硬件故障应对方案
- 🔒 安全:备份加密与访问控制
- 🔧 PGSQL 备份恢复:详细操作指南