PostgreSQL: PostgreSQL备份恢复与高可用
最后更新:2026-08-26
1. 你将学到
- pg_dump / pg_dumpall 逻辑备份
- pg_restore 恢复
- COPY 命令导入/导出(CSV / Binary)
- WAL(Write-Ahead Log)原理
- PITR 时间点恢复(PG 特色)
- 流复制(Streaming Replication)
- 逻辑复制(PG 特色:按表复制)
- pg_basebackup 物理备份
- 备份策略:全量 + 增量
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 保证数据完整性和支持恢复的核心机制。
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. 流程图:备份恢复决策
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 只读取归档目录不执行恢复,是验证备份完整性的快速方法。损坏时需从其他备份恢复。
📖 小节
- pg_dump 逻辑备份支持自定义/目录/纯 SQL 三种格式,-Fc 压缩格式最常用
- pg_restore 从自定义/目录格式恢复,支持并行、选择性恢复、--clean 清理
- COPY / \copy 快速导入导出 CSV,COPY 服务端执行,\copy 客户端执行
- WAL 是 PG 恢复基础:先写日志再写数据,保证崩溃恢复能力
- PITR(PG 特色)通过基础备份 + WAL 归档恢复到任意时间点,实现零数据丢失
- 流复制实时同步物理 WAL,standby 可做读副本
- 逻辑复制(PG 特色)按表级别复制,支持跨版本、目标端可写
- pg_basebackup 物理备份是 PITR 和流复制的基础
- 生产环境推荐 PITR + 流复制组合方案,定期验证备份完整性
📝 作业
-
⭐ 使用
pg_dump -Fc备份shop_db数据库,然后用pg_restore --list验证备份完整性,再恢复到shop_db_test数据库。 -
⭐⭐ 编写自动化备份脚本:每日 pg_dump 全量备份(自定义格式压缩),保留 7 天,备份后用
pg_restore --list验证。同时用COPY导出orders表为 CSV(含表头),按日期命名。 -
⭐⭐⭐ 配置完整的 PITR 方案:启用 WAL 归档(
archive_command到指定目录),执行pg_basebackup基础备份,模拟误删数据后恢复到指定时间点。记录 LSN 和恢复时间,验证数据完整性。额外配置流复制 standby 并验证读查询可用。