PostgreSQL: PostgreSQL备份恢复与高可用

最后更新:2026-08-26

1. 你将学到


2. 故事

Bob 是电商平台的 DBA。凌晨 3 点,一位开发人员误执行了 DELETE FROM products WHERE category = 'Electronics',删除了价值 2 million USD 的产品数据。

Bob 需要在最短时间内恢复到 2:59 AM 的状态——即误删除前一分钟,实现零数据丢失。他选择 PITR(Point-in-Time Recovery)方案。


3. Concept:逻辑备份

(1) pg_dump 选项

选项 含义 示例
-Fc 自定义格式(压缩,推荐) pg_dump -Fc db > db.dump
-Fd 目录格式(并行备份) pg_dump -Fd db -f dir/
-Fp 纯 SQL 文本 pg_dump -Fp db > db.sql
-j N 并行任务数(需 -Fd) pg_dump -Fd db -j 4 -f dir/
-t table 只备份指定表 pg_dump -t products db > p.dump
-n schema 只备份指定 schema pg_dump -n public db > s.dump
--exclude-table 排除表 pg_dump --exclude-table=logs db > d.dump
-Z 0-9 压缩级别 pg_dump -Fc -Z6 db > db.dump

▶ 示例:pg_dump 备份单个数据库

BASH
# Custom format (recommended, compressed, parallel restore possible)
pg_dump -h localhost -U postgres -Fc shop_db > /backup/shop_db_$(date +%Y%m%d).dump

# Directory format with 4 parallel workers
pg_dump -h localhost -U postgres -Fd shop_db -j 4 -f /backup/shop_db_dir/

# Plain SQL format (human readable, can edit before restore)
pg_dump -h localhost -U postgres -Fp shop_db > /backup/shop_db.sql

输出:

TEXT 📖 仅展示
# 命令执行成功

▶ 示例:pg_dump 备份指定表

BASH
# Backup only orders and order_items tables
pg_dump -h localhost -U postgres -Fc \
  -t orders -t order_items \
  shop_db > /backup/order_tables.dump

# Backup all tables matching pattern
pg_dump -h localhost -U postgres -Fc \
  -t 'order_*' \
  shop_db > /backup/order_prefix_tables.dump

# Exclude large log tables
pg_dump -h localhost -U postgres -Fc \
  --exclude-table='access_log_*' \
  shop_db > /backup/shop_no_logs.dump

输出:

TEXT 📖 仅展示
# 命令执行成功

(2) pg_dumpall vs pg_dump

维度 pg_dump pg_dumpall
备份范围 单个数据库 整个集群(所有数据库)
角色信息 不含 包含角色/表空间定义
输出格式 可选 -Fc/-Fd/-Fp 仅纯 SQL(-Fp)
并行 支持(-j) 不支持
推荐场景 单库日常备份 角色备份 + 全集群迁移

▶ 示例:pg_dumpall 全集群备份

BASH
# Backup all databases and roles (plain SQL only)
pg_dumpall -h localhost -U postgres > /backup/cluster_full_$(date +%Y%m%d).sql

# Backup only roles (useful for migration)
pg_dumpall -h localhost -U postgres --roles-only > /backup/roles_only.sql

# Backup only tablespace definitions
pg_dumpall -h localhost -U postgres --tablespaces-only > /backup/tablespaces.sql

输出:

TEXT 📖 仅展示
# 命令执行成功

4. Concept:逻辑恢复

(1) pg_restore 选项

选项 含义 适用格式
-d db 恢复到指定数据库 -Fc / -Fd
-j N 并行恢复 -Fc / -Fd
--clean 先 DROP 再 CREATE -Fc / -Fd
--if-exists DROP IF EXISTS(配合 --clean) -Fc / -Fd
-t table 只恢复指定表 -Fc / -Fd
--list 列出归档内容 -Fc / -Fd
--section=pre-data 只恢复预数据(schema) -Fc / -Fd

▶ 示例:pg_restore 从自定义格式恢复

BASH
# Restore to a new database
createdb -h localhost -U postgres shop_db_restore
pg_restore -h localhost -U postgres -d shop_db_restore /backup/shop_db.dump

# Parallel restore (4 workers, directory format)
pg_restore -h localhost -U postgres -d shop_db -j 4 /backup/shop_db_dir/

# Restore with clean (drop existing objects first)
pg_restore -h localhost -U postgres -d shop_db \
  --clean --if-exists /backup/shop_db.dump

输出:

TEXT 📖 仅展示
# 命令执行成功

▶ 示例:选择性恢复

BASH
# List archive contents to find table entry numbers
pg_restore --list /backup/shop_db.dump

# Restore only specific tables by name
pg_restore -h localhost -U postgres -d shop_db \
  -t products -t categories \
  /backup/shop_db.dump

# Restore only schema (no data)
pg_restore -h localhost -U postgres -d shop_db \
  --section=pre-data /backup/shop_db.dump

输出:

TEXT 📖 仅展示
# 命令执行成功

(2) pg_restore vs psql < file.sql

维度 pg_restore psql < file.sql
输入格式 -Fc / -Fd 纯 SQL 文本
并行 支持(-j) 不支持
选择性恢复 支持(-t / --list) 不支持
清理旧数据 --clean 需手动写 DROP
推荐场景 自定义/目录格式 pg_dumpall 输出

▶ 示例:从纯 SQL 恢复

BASH
# Restore from pg_dumpall output
psql -h localhost -U postgres -f /backup/cluster_full_20250601.sql

# Restore from pg_dump plain format
createdb -h localhost -U postgres shop_db_new
psql -h localhost -U postgres -d shop_db_new -f /backup/shop_db.sql

输出:

TEXT 📖 仅展示
# psql 命令执行成功

5. Concept:COPY 导入导出

(1) COPY vs \copy

命令 执行位置 文件访问 权限要求
COPY (SQL) 服务器端 读服务器文件系统 超级用户
\copy (psql) 客户端 读客户端文件 普通用户

▶ 示例:COPY 导出为 CSV

SQL
-- Export to CSV with header
COPY (SELECT order_id, customer_id, total_amount, order_date
      FROM orders
      WHERE order_status = 'completed'
      ORDER BY order_date DESC)
TO '/tmp/completed_orders.csv'
WITH (FORMAT csv, HEADER true, DELIMITER ',');

-- Export entire table
COPY products TO '/tmp/products.csv' WITH (FORMAT csv, HEADER true);

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

▶ 示例:COPY 导入 CSV

SQL
-- Import from CSV
COPY products(product_name, unit_price, category, stock_qty)
FROM '/data/new_products.csv'
WITH (FORMAT csv, HEADER true, DELIMITER ',');

-- Import with error handling (PG 17+)
COPY products(product_name, unit_price, category, stock_qty)
FROM '/data/new_products.csv'
WITH (FORMAT csv, HEADER true, ON_ERROR ignore);

输出:

TEXT 📖 仅展示
-- SQL 语句执行成功

(2) COPY 格式选项

选项 说明
FORMAT csv / text / binary 输出格式
HEADER true / false 首行为列名
DELIMITER ',' / '\t' 分隔符(csv 默认逗号)
QUOTE '"' 引用字符
NULL '' NULL 表示字符串
ENCODING 'UTF8' 文件编码

▶ 示例:二进制格式导出导入

SQL
-- Binary export (faster, smaller)
COPY orders TO '/tmp/orders.bin' WITH (FORMAT binary);

-- Binary import
COPY orders FROM '/tmp/orders.bin' WITH (FORMAT binary);

输出:

TEXT 📖 仅展示
-- SQL 语句执行成功

6. Concept:WAL 与 PITR

(1) WAL(Write-Ahead Log)原理

WAL 是 PG 保证数据完整性和支持恢复的核心机制。

100%
flowchart LR
    A[Client<br/>WRITE] --> B[WAL Buffer<br/>先写日志]
    B --> C[WAL File<br/>持久化到磁盘]
    B --> D[Shared Buffer<br/>后写数据]
    D --> E[Data File<br/>Checkpoint 落盘]

    style B fill:#ffcdd2
    style C fill:#ff8a80
    style D fill:#c8e6c9
    style E fill:#a5d6a7
概念 说明
WAL segment WAL 文件,默认 16MB 一个
LSN Log Sequence Number,日志位置标识
Checkpoint 将内存脏页刷盘,回收 WAL
wal_level WAL 详细程度:replica / logical
archive_mode 是否归档 WAL 文件
archive_command 归档到指定路径

(2) PITR 配置步骤

步骤 操作 说明
1 启用 WAL 归档 archive_mode = on
2 设置归档命令 archive_command = 'cp %p /archive/%f'
3 基础备份 pg_basebackup
4 持续归档 WAL 文件自动归档
5 恢复到时间点 recovery_target_time

▶ 示例:配置 WAL 归档

BASH
# postgresql.conf settings
cat >> /etc/postgresql/16/main/postgresql.conf << 'EOF'
wal_level = replica
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/archive/%f'
archive_timeout = 300
max_wal_senders = 3
EOF

# Restart PostgreSQL
pg_ctlcluster 16 main restart

输出:

TEXT 📖 仅展示
# 命令执行成功

▶ 示例:pg_basebackup 物理基础备份

BASH
# Full physical backup (base for PITR)
pg_basebackup -h localhost -U replicator \
  -D /backup/base_$(date +%Y%m%d) \
  -Ft -z -P \
  --checkpoint=fast

# -Ft: tar format
# -z: gzip compression
# -P: show progress
# --checkpoint=fast: force checkpoint before backup

输出:

TEXT 📖 仅展示
# 命令执行成功

▶ 示例:PITR 时间点恢复

Bob 恢复到误删除前一分钟(2:59 AM):

BASH
# Step 1: Stop PostgreSQL
pg_ctlcluster 16 main stop

# Step 2: Clear existing data directory
rm -rf /var/lib/postgresql/16/main/*

# Step 3: Restore base backup
tar -xzf /backup/base_20250601/base.tar.gz \
  -C /var/lib/postgresql/16/main/

# Step 4: Create recovery configuration
cat >> /var/lib/postgresql/16/main/postgresql.auto.conf << 'EOF'
restore_command = 'cp /var/lib/postgresql/archive/%f %p'
recovery_target_time = '2025-06-01 02:59:00'
recovery_target_action = 'promote'
EOF

# Step 5: Create recovery signal file
touch /var/lib/postgresql/16/main/recovery.signal

# Step 6: Start PostgreSQL (will recover to target time)
pg_ctlcluster 16 main start

# PostgreSQL will replay WAL up to 02:59:00 then promote

输出:

TEXT 📖 仅展示
# 命令执行成功

(3) 逻辑备份 vs 物理备份 vs PITR

维度 逻辑备份(pg_dump) 物理备份(pg_basebackup) PITR
粒度 表/库 整个集群 整个集群
恢复精度 备份时刻 备份时刻 任意时间点
恢复速度 慢(逐条 SQL) 快(文件拷贝) 中等(重放 WAL)
存储空间 小(压缩) 大(完整副本) 中(基础+归档)
零丢失
在线备份

7. Concept:复制与高可用

(1) 流复制 vs 逻辑复制

维度 流复制(Streaming Replication) 逻辑复制(Logical Replication)
复制级别 物理 WAL 块 逻辑变更(INSERT/UPDATE/DELETE)
粒度 整个集群 指定表/发布
版本要求 主从版本需一致 可跨大版本
DDL 复制 自动 不复制 DDL
目标端可写 否(只读 standby)
PG 特色 PG 原生逻辑复制(10+)

▶ 示例:配置流复制

BASH
# On primary: postgresql.conf
wal_level = replica
max_wal_senders = 5
wal_keep_size = '1GB'

# On primary: pg_hba.conf
echo 'host replication replicator 192.168.1.0/24 md5' >> pg_hba.conf

# On primary: create replication user
psql -c "CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'RepP@ss';"

# On standby: take base backup
pg_basebackup -h primary_host -U replicator \
  -D /var/lib/postgresql/16/main -Fp -Xs -P -R

# -R: create standby.signal and auto-configure
# standby will auto-connect to primary on startup

输出:

TEXT 📖 仅展示
# psql 命令执行成功

▶ 示例:配置逻辑复制

SQL
-- On publisher (source database)
CREATE PUBLICATION pub_orders FOR TABLE orders, order_items;

-- Or publish all tables in a schema
CREATE PUBLICATION pub_all FOR ALL TABLES;

-- On subscriber (target database)
CREATE SUBSCRIPTION sub_orders
  CONNECTION 'host=primary_host dbname=shop_db user=replicator password=RepP@ss'
  PUBLICATION pub_orders;

-- Check replication status
SELECT * FROM pg_stat_replication;       -- on publisher
SELECT * FROM pg_stat_subscription;      -- on subscriber

输出:

TEXT 📖 仅展示
CREATE TABLE

(2) 复制监控视图

视图 位置 内容
pg_stat_replication 主库 所有连接的 standby 状态
pg_stat_wal_receiver 从库 WAL 接收状态
pg_stat_subscription 订阅端 逻辑复制订阅状态
pg_replication_slots 主库 复制槽信息

▶ 示例:监控流复制延迟

SQL
-- Check replication lag on primary
SELECT
  client_addr,
  state,
  sent_lsn,
  write_lsn,
  flush_lsn,
  replay_lsn,
  (sent_lsn - replay_lsn) AS replication_lag
FROM pg_stat_replication;

-- Check if standby is in recovery mode
SELECT pg_is_in_recovery();

输出:

TEXT 📖 仅展示
 id | name     | value 
----+----------+-------
  1 | example  | 42
(1 row)

8. Concept:备份策略

(1) 全量 + 增量方案对比

策略 方法 恢复时间 存储开销 零丢失
逻辑全量 pg_dump 定时
物理全量 pg_basebackup 定时
PITR 基础备份 + WAL 归档
流复制 standby 实时同步 最快 近乎零丢失

▶ 示例:自动化备份脚本

BASH
#!/bin/bash
# Daily backup script for shop_db

BACKUP_DIR="/backup/daily"
DATE=$(date +%Y%m%d_%H%M%S)
RETAIN_DAYS=7

# Full logical backup (pg_dump custom format)
pg_dump -h localhost -U postgres -Fc -Z6 \
  shop_db > ${BACKUP_DIR}/shop_db_${DATE}.dump

# Backup roles separately
pg_dumpall -h localhost -U postgres --roles-only \
  > ${BACKUP_DIR}/roles_${DATE}.sql

# Cleanup old backups (retain 7 days)
find ${BACKUP_DIR} -name "*.dump" -mtime +${RETAIN_DAYS} -delete
find ${BACKUP_DIR} -name "*.sql" -mtime +${RETAIN_DAYS} -delete

echo "Backup completed: shop_db_${DATE}.dump"

输出:

TEXT 📖 仅展示
# 命令执行成功

(2) 生产环境推荐策略

环境 推荐方案 RPO RTO
开发 pg_dump 每日 24h 小时级
测试 pg_dump + 流复制 分钟级 分钟级
生产 PITR + 流复制 接近零 分钟级
关键生产 PITR + 流复制 + 逻辑复制 秒级

▶ 示例:验证备份完整性

BASH
# Verify pg_dump backup is readable
pg_restore --list /backup/shop_db.dump > /dev/null
if [ $? -eq 0 ]; then
  echo "Backup verified OK"
else
  echo "Backup CORRUPT - alert ops team!"
fi

# Verify WAL archive is not falling behind
psql -c "SELECT pg_current_wal_lsn(), pg_last_archive_lsn();"

输出:

TEXT 📖 仅展示
# psql 命令执行成功

9. 流程图:备份恢复决策

100%
flowchart TD
    A[需要备份/恢复?] --> B{恢复场景?}
    B -->|误删表/数据| C{有逻辑备份?}
    B -->|整个库崩溃| D{有PITR?}
    B -->|计划迁移| E{数据量?}
    C -->|是| F[pg_restore -t table]
    C -->|否| G{有流复制standby?}
    G -->|是| H[从standby导出数据]
    G -->|否| I[数据无法恢复]
    D -->|是| J[PITR恢复到目标时间]
    D -->|否| K[pg_basebackup恢复到备份时刻]
    E -->|小| L[pg_dump/pg_restore]
    E -->|大| M[pg_basebackup + 流复制]
    J --> N{需要跨版本?}
    N -->|是| O[逻辑复制迁移]
    N -->|否| P[物理恢复]

    style I fill:#ffcdd2
    style J fill:#c8e6c9
    style O fill:#bbdefb

10. 综合示例

Bob 的 PITR 恢复完整流程——凌晨 3 点误删 products 后恢复到 2:59 AM:

SQL
-- Step 1: Confirm the accident time and data loss
SELECT pg_current_wal_lsn();  -- note current LSN
SELECT COUNT(*) FROM products WHERE category = 'Electronics';  -- verify loss

-- Step 2: Verify WAL archive has the needed segments
SELECT pg_last_archive_lsn();
-- Should be >= LSN at 02:59 AM
BASH
# Step 3: Stop PostgreSQL immediately to preserve state
pg_ctlcluster 16 main stop

# Step 4: Preserve current WAL files (do NOT delete!)
cp -r /var/lib/postgresql/16/main/pg_wal /tmp/pg_wal_backup/

# Step 5: Restore base backup
rm -rf /var/lib/postgresql/16/main/*
tar -xzf /backup/base_20250531/base.tar.gz \
  -C /var/lib/postgresql/16/main/

# Step 6: Copy preserved WAL back for complete recovery
cp /tmp/pg_wal_backup/* /var/lib/postgresql/16/main/pg_wal/

# Step 7: Configure PITR target
cat >> /var/lib/postgresql/16/main/postgresql.auto.conf << 'EOF'
restore_command = 'cp /var/lib/postgresql/archive/%f %p'
recovery_target_time = '2025-06-01 02:59:00'
recovery_target_action = 'promote'
EOF

touch /var/lib/postgresql/16/main/recovery.signal

# Step 8: Start and verify
pg_ctlcluster 16 main start

# Step 9: Verify recovery
psql -c "SELECT COUNT(*) FROM products WHERE category = 'Electronics';"
SQL
-- Step 10: After successful recovery, take a fresh backup
-- Run in psql after verifying data integrity
SELECT pg_switch_wal();  -- force WAL switch for clean archive point
BASH
# Step 11: Take new base backup for future PITR
pg_basebackup -h localhost -U postgres \
  -D /backup/base_$(date +%Y%m%d) -Ft -z -P

❓ 常见问题

Q pg_dump 备份时会锁表吗?
A pg_dump 使用快照(MVCC),不锁表,不影响读写操作。但备份的是开始时刻的数据快照,备份期间的新增数据不包含在内。
Q PITR 能恢复到删除单条记录之前吗?
A 可以,只要 WAL 归档覆盖了该时间段。PITR 的最小粒度是事务级别——recovery_target_xid 可精确到某个事务。但无法跳过某些事务只回滚特定操作。
Q WAL 归档文件能删吗?
A 已完成 Checkpoint 且不再被 standby 需要的 WAL 可以删除。但 PITR 恢复需要从基础备份到目标时间的所有 WAL 段,删除前确认不再需要恢复。
Q 流复制的 standby 能做读查询吗?
A 可以。设置 hot_standby = on 后,standby 可接受只读查询(SELECT),称为"读副本",适合分担分析查询负载。
Q 逻辑复制能跨 PostgreSQL 版本吗?
A 可以。逻辑复制发送逻辑变更(SQL 操作),不依赖物理格式,支持跨大版本升级迁移。这是 PG 逻辑复制的重要优势。
Q pg_basebackup 和 pg_dump 该选哪个?
A 需要 PITR 或完整集群恢复选 pg_basebackup(物理备份);需要选择性恢复、跨版本迁移、单表导出选 pg_dump(逻辑备份)。生产环境通常两者都用。
Q COPY 和 INSERT 哪个导入更快?
A COPY 快得多。COPY 是批量写入,单次协议往返;INSERT 逐行解析和执行。大数据导入优先 COPY,小量数据用 INSERT 更灵活。
Q 备份脚本的 pg_restore --list 报错说明什么?
A 备份文件可能损坏或不完整。--list 只读取归档目录不执行恢复,是验证备份完整性的快速方法。损坏时需从其他备份恢复。

📖 小节


📝 作业

  1. ⭐ 使用 pg_dump -Fc 备份 shop_db 数据库,然后用 pg_restore --list 验证备份完整性,再恢复到 shop_db_test 数据库。

  2. ⭐⭐ 编写自动化备份脚本:每日 pg_dump 全量备份(自定义格式压缩),保留 7 天,备份后用 pg_restore --list 验证。同时用 COPY 导出 orders 表为 CSV(含表头),按日期命名。

  3. ⭐⭐⭐ 配置完整的 PITR 方案:启用 WAL 归档(archive_command 到指定目录),执行 pg_basebackup 基础备份,模拟误删数据后恢复到指定时间点。记录 LSN 和恢复时间,验证数据完整性。额外配置流复制 standby 并验证读查询可用。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏