A-speed

每个人都是🏆
  新随笔  :: 管理

PostgreSQL不完全恢复实验:备份恢复实战

Posted on 2026-03-20 19:46  a-speed  阅读(5)  评论(0)    收藏  举报

PostgreSQL 不完全恢复实验:备份恢复实战

概述

本文在 PostgreSQL 中模拟类似 Oracle 不完全恢复的场景,通过 pg_dump 逻辑备份和 pg_restore 恢复的方式,实现数据的时间点恢复。同时介绍 PostgreSQL 的 PITR(Point-In-Time Recovery)机制。


一、实验环境

项目 信息
数据库 PostgreSQL 16.13
操作系统 Ubuntu 24.04.4 LTS
WAL级别 replica
归档模式 已开启

前置条件检查

-- 检查WAL级别(需要replica或logical)
SHOW wal_level;

-- 检查归档模式
SHOW archive_mode;

-- 检查归档命令
SHOW archive_command;

-- 检查当前WAL位置
SELECT pg_current_wal_lsn();

二、PostgreSQL 备份恢复方式对比

方式 类型 说明 适用场景
pg_dump 逻辑备份 单数据库备份,SQL格式 小型数据库、迁移
pg_dumpall 逻辑备份 整个集群备份 全集群迁移
pg_basebackup 物理备份 文件系统级备份 PITR、主从复制
pgBackRest 物理备份 第三方备份工具 企业级备份
WAL归档 增量备份 配合基础备份使用 PITR恢复

三、实验流程图

┌─────────────────────────────────────────────────────────────────────────────┐
│                        PostgreSQL 备份恢复实验流程                            │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  时间线:                                                                    │
│  ──────────────────────────────────────────────────────────────────────▶   │
│                                                                             │
│  T1: 初始数据      T2: pg_dump备份    T3: 正常操作      T4: 误操作           │
│  ┌─────────┐      ┌─────────┐       ┌─────────┐      ┌─────────┐           │
│  │ 5条记录  │ ──▶ │ pg_dump  │ ───▶ │ INSERT  │ ──▶ │ DELETE  │           │
│  │         │      │ -F c     │       │ UPDATE  │      │ 全表删除 │           │
│  └─────────┘      └─────────┘       └─────────┘      └─────────┘           │
│                          │                                     │             │
│                          ▼                                     ▼             │
│                   ┌─────────────┐                       ┌───────────┐       │
│                   │ 备份文件     │                       │ 恢复目标  │       │
│                   │ .dump       │                       │ 时间点 T2 │       │
│                   └─────────────┘                       └───────────┘       │
│                          │                                     ▲             │
│                          │         ┌─────────────┐            │             │
│                          └────────▶│ pg_restore  │────────────┘             │
│                                    │ 恢复数据    │                          │
│                                    └─────────────┘                          │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

四、详细步骤

步骤1:开启归档模式(PITR前提)

# 创建归档目录
sudo mkdir -p /var/lib/postgresql/16/archive
sudo chown postgres:postgres /var/lib/postgresql/16/archive

# 修改postgresql.conf配置
sudo tee -a /etc/postgresql/16/main/postgresql.conf > /dev/null << 'EOF'

# PITR Archive Settings
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/16/archive/%f'
EOF

# 重启PostgreSQL
sudo systemctl restart postgresql

# 验证配置
psql -c "SHOW archive_mode;"
psql -c "SHOW archive_command;"

验证结果:

 archive_mode 
--------------
 on

             archive_command             
-----------------------------------------
 cp %p /var/lib/postgresql/16/archive/%f

步骤2:准备测试数据

-- 创建测试数据库
DROP DATABASE IF EXISTS recovery_test;
CREATE DATABASE recovery_test;

-- 连接到测试数据库
\c recovery_test

-- 创建测试表
CREATE TABLE employees (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    department VARCHAR(50),
    salary DECIMAL(10,2),
    hire_date DATE,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 插入初始数据
INSERT INTO employees (name, department, salary, hire_date) VALUES
('张三', '技术部', 15000.00, '2020-01-15'),
('李四', '销售部', 12000.00, '2020-03-20'),
('王五', '财务部', 13000.00, '2020-06-10'),
('赵六', '技术部', 16000.00, '2020-08-25'),
('钱七', '人事部', 11000.00, '2021-01-05');

初始数据:

id name department salary hire_date
1 张三 技术部 15000.00 2020-01-15
2 李四 销售部 12000.00 2020-03-20
3 王五 财务部 13000.00 2020-06-10
4 赵六 技术部 16000.00 2020-08-25
5 钱七 人事部 11000.00 2021-01-05

步骤3:执行pg_dump备份

# 创建备份目录
mkdir -p /tmp/pg_pitr_backup

# 执行pg_dump备份(自定义格式)
pg_dump -U itdba -h localhost -d recovery_test \
  -F c \
  -f /tmp/pg_pitr_backup/base_backup_20260320.dump

# 查看备份文件
ls -la /tmp/pg_pitr_backup/

关键参数说明:

参数 说明
-F c 自定义格式(压缩,支持并行恢复)
-F t tar格式
-F p 纯SQL文本格式
-F d 目录格式(支持并行备份)
-j N 并行工作线程数(目录格式)

备份时间点: 2026-03-20 11:42:51


步骤4:正常业务操作

-- 插入新员工
INSERT INTO employees (name, department, salary, hire_date) VALUES
('孙八', '技术部', 14000.00, '2021-03-15'),
('周九', '销售部', 12500.00, '2021-05-20');

-- 更新薪资(技术部涨薪10%)
UPDATE employees SET salary = salary * 1.1 WHERE department = '技术部';

正常操作后数据:

id name department salary hire_date
1 张三 技术部 16500.00 2020-01-15
2 李四 销售部 12000.00 2020-03-20
3 王五 财务部 13000.00 2020-06-10
4 赵六 技术部 17600.00 2020-08-25
5 钱七 人事部 11000.00 2021-01-05
6 孙八 技术部 15400.00 2021-03-15
7 周九 销售部 12500.00 2021-05-20

正常操作完成时间: 2026-03-20 11:42:53


步骤5:模拟误操作

-- ⚠️ 模拟误操作:忘记WHERE条件,删除全表
DELETE FROM employees;

误操作后数据:

(空表,0条记录)

误操作时间: 2026-03-20 11:42:56


步骤6:执行恢复

# 步骤1:删除当前数据库
psql -U itdba -d postgres -c "DROP DATABASE IF EXISTS recovery_test;"

# 步骤2:重新创建数据库
psql -U itdba -d postgres -c "CREATE DATABASE recovery_test;"

# 步骤3:恢复备份
pg_restore -U itdba -h localhost -d recovery_test \
  /tmp/pg_pitr_backup/base_backup_20260320.dump

步骤7:验证恢复结果

-- 查看恢复后的数据
SELECT * FROM employees;

-- 统计数据
SELECT COUNT(*) AS total, SUM(salary) AS total_salary FROM employees;

恢复后数据:

id name department salary hire_date
1 张三 技术部 15000.00 2020-01-15
2 李四 销售部 12000.00 2020-03-20
3 王五 财务部 13000.00 2020-06-10
4 赵六 技术部 16000.00 2020-08-25
5 钱七 人事部 11000.00 2021-01-05

五、恢复结果对比

阶段 记录数 薪资总计 说明
初始数据 5 67000.00 备份前
正常操作后 7 98000.00 误操作前
误操作后 0 0 DELETE全表
恢复后 5 67000.00 ✅ 恢复到备份点

六、PostgreSQL PITR(基于时间点恢复)

6.1 PITR原理

┌─────────────────────────────────────────────────────────────────────────────┐
│                        PostgreSQL PITR 恢复原理                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   ┌─────────────┐     ┌─────────────┐     ┌─────────────┐                  │
│   │ pg_basebackup│     │  WAL归档    │     │  恢复目标   │                  │
│   │  (基础备份)  │     │  (增量日志) │     │  (时间点)   │                  │
│   └──────┬──────┘     └──────┬──────┘     └──────┬──────┘                  │
│          │                   │                   │                          │
│          ▼                   ▼                   ▼                          │
│   ┌─────────────────────────────────────────────────────────────┐          │
│   │                      恢复流程                                │          │
│   │  1. 恢复基础备份到数据目录                                   │          │
│   │  2. 配置recovery_target_time/recovery_target_lsn            │          │
│   │  3. 启动PostgreSQL,自动重放WAL到目标点                      │          │
│   │  4. 数据库恢复到指定时间点状态                               │          │
│   └─────────────────────────────────────────────────────────────┘          │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

6.2 PITR恢复步骤

# 1. 停止PostgreSQL服务
sudo systemctl stop postgresql

# 2. 清空数据目录
sudo rm -rf /var/lib/postgresql/16/main/*

# 3. 恢复基础备份
sudo tar -xzf /backup/base.tar.gz -C /var/lib/postgresql/16/main/

# 4. 创建recovery配置
cat > /var/lib/postgresql/16/main/postgresql.auto.conf << 'EOF'
restore_command = 'cp /var/lib/postgresql/16/archive/%f %p'
recovery_target_time = '2026-03-20 11:42:53'
recovery_target_action = 'promote'
EOF

# 5. 创建恢复信号文件
touch /var/lib/postgresql/16/main/recovery.signal

# 6. 启动PostgreSQL
sudo systemctl start postgresql

# 7. 验证恢复
psql -c "SELECT * FROM employees;"

6.3 恢复目标选项

参数 说明
recovery_target_time 恢复到指定时间点
recovery_target_xid 恢复到指定事务ID
recovery_target_lsn 恢复到指定WAL位置
recovery_target_name 恢复到命名恢复点
recovery_target_immediate 恢复到最早一致点后停止

七、与MySQL/Oracle对比

对比项 Oracle MySQL PostgreSQL
恢复类型 不完全恢复 PITR PITR
物理备份 RMAN mysqlbackup pg_basebackup
逻辑备份 expdp mysqldump pg_dump
增量日志 归档日志 Binlog WAL
时间点恢复 SET UNTIL TIME --stop-datetime recovery_target_time
SCN/LSN恢复 SET UNTIL SCN --stop-position recovery_target_lsn
表级恢复 表空间传输 binlog过滤 pg_restore -t

八、最佳实践建议

8.1 备份策略

策略 说明
基础备份频率 每天至少一次
WAL归档 实时归档到远程存储
备份保留 至少保留7天
备份验证 定期进行恢复演练

8.2 归档配置建议

-- 推荐的归档配置
archive_mode = on
archive_command = 'rsync -a %p backup_server:/archive/%f'
archive_timeout = 300  -- 5分钟强制切换
wal_keep_size = 1GB    -- 保留足够的WAL

8.3 恢复流程

1. 立即停止应用写入
2. 记录误操作时间点
3. 备份当前WAL文件
4. 恢复基础备份
5. 配置恢复目标
6. 启动数据库重放WAL
7. 验证数据完整性
8. 恢复应用服务

8.4 注意事项

注意项 说明
wal_level 需要设置为replica或logical
archive_mode 必须开启才能PITR
WAL保留 确保WAL文件不被删除
恢复测试 生产操作前先测试验证

九、常用命令速查

9.1 备份相关

# 逻辑备份(自定义格式)
pg_dump -U postgres -F c -f backup.dump database_name

# 逻辑备份(SQL文本)
pg_dump -U postgres -F p -f backup.sql database_name

# 整个集群备份
pg_dumpall -U postgres > cluster_backup.sql

# 物理备份
pg_basebackup -h localhost -D /backup/base -Ft -z -Xs -P

9.2 恢复相关

# 恢复自定义格式备份
pg_restore -U postgres -d database_name backup.dump

# 恢复SQL文本
psql -U postgres -d database_name < backup.sql

# 并行恢复
pg_restore -U postgres -d database_name -j 4 backup.dump

# 仅恢复表结构
pg_restore -U postgres -s -d database_name backup.dump

9.3 WAL相关

-- 查看当前WAL位置
SELECT pg_current_wal_lsn();

-- 查看WAL文件列表
SELECT * FROM pg_ls_waldir();

-- 手动切换WAL
SELECT pg_switch_wal();

-- 创建恢复点
SELECT pg_create_restore_point('before_upgrade');

十、总结

PostgreSQL 提供了多种备份恢复方式:

  1. pg_dump/pg_restore:逻辑备份,适合小型数据库和单库恢复
  2. pg_basebackup + WAL归档:物理备份,支持PITR时间点恢复
  3. 第三方工具:pgBackRest、Barman等企业级备份方案

关键要点:

  • 开启归档模式是PITR的前提
  • 定期执行基础备份
  • WAL文件需要妥善保存
  • 恢复前备份当前状态
  • 恢复后验证数据完整性