
PostgreSQL 备份恢复演练的价值,不只是“有一份备份文件”,而是帮助你确认在误删表、程序写错数据、服务器磁盘故障时,能不能按预期恢复到一个明确时间点。本文教你用 pg_dump(一种 PostgreSQL 逻辑备份工具)和 WAL(Write-Ahead Logging,预写日志)建立可验证的恢复流程,并用具体命令检查恢复点是否真的可用。
很多团队会定时生成 dump 文件,却很少做恢复演练。等到事故发生时,才发现备份文件缺少角色权限、WAL(Write-Ahead Logging,预写日志)没有连续归档,或者恢复后的业务数据对不上。对于运行在 VPS(虚拟专用服务器)入侵应急处理之后需要核对数据库完整性的站点,恢复演练尤其关键:它能把“我觉得可以恢复”变成“我已经恢复并校验过”。
先明确:两类备份分别解决什么问题
pg_dump(一种 PostgreSQL 逻辑备份工具)适合导出单库、跨环境迁移和开发环境复刻,默认恢复到导出时刻。WAL(Write-Ahead Logging,预写日志)配合基础备份,可以把数据库推进到指定时间点。前者解决“有没有一份可迁移备份”,后者解决“能否回到事故前几秒”。

演练环境:把变量固定下来,避免结果不可复现
恢复演练应先从一套隔离环境开始,不要直接在生产库上测试;如果服务器资源偏紧,可先参考 网站性能与 TTFB 优化指南 里的容量排查方式,确认 CPU(中央处理器)、内存和磁盘 I/O(输入输出)是否足够支撑恢复测试。建议准备一台与生产接近的测试服务器,PostgreSQL 主版本保持一致,磁盘空间至少预留“数据库大小 × 2”的容量。例如生产数据目录为 40 GB,测试机最好预留 80 GB 以上,用来同时放置基础备份、WAL(Write-Ahead Logging,预写日志)归档和恢复后的数据目录。
演练前先记录三个基础信息:PostgreSQL 版本、数据库名、备份保存路径。下面命令可以快速取得版本与库大小,后续写入演练记录,方便下一次对比。
psql -U postgres -c "SELECT version();"
psql -U postgres -c "SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database WHERE datistemplate = false;"
mkdir -p /backup/pg/dump /backup/pg/wal /restore/pgdata
用 pg_dump 做一份可恢复的逻辑备份
逻辑备份建议优先使用 custom 格式,因为它可以配合 pg_restore(一种 PostgreSQL 逻辑恢复工具)并行恢复,也能按对象查看和选择恢复。下面命令把 appdb 数据库导出到带日期的文件,并把错误直接写入日志。
export PGDATABASE=appdb
export PGUSER=postgres
pg_dump -Fc -f /backup/pg/dump/appdb_20260812.dump "$PGDATABASE" 2>/backup/pg/dump/appdb_20260812.log
ls -lh /backup/pg/dump/appdb_20260812.dump
pg_restore -l /backup/pg/dump/appdb_20260812.dump > /backup/pg/dump/appdb_20260812.list
- 如果数据库有大对象,执行
SELECT count(*) FROM pg_largeobject_metadata;,确认备份策略覆盖它们。 - 如果应用依赖扩展,执行
SELECT extname FROM pg_extension;,恢复环境要提前安装同名扩展。 - 如果使用独立角色,执行
pg_dumpall --roles-only > /backup/pg/dump/roles.sql,避免恢复时缺少 owner。
开启 WAL 归档并验证连续性
WAL(Write-Ahead Logging,预写日志)归档的关键不是“目录里有文件”,而是文件能连续覆盖故障窗口。先检查数据库参数,再把归档命令写得足够保守,避免覆盖同名文件。
SHOW wal_level;
SHOW archive_mode;
SHOW archive_command;
SHOW data_directory;
如果尚未开启归档,可在 PostgreSQL 配置文件中设置以下参数,然后重启数据库。示例路径需要按你的系统实际位置调整,例如部分 Linux 发行版会把配置放在 /etc/postgresql/16/main/postgresql.conf。
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /backup/pg/wal/%f && cp %p /backup/pg/wal/%f'
archive_timeout = 300
archive_timeout = 300 表示最长 5 分钟切一次 WAL(Write-Ahead Logging,预写日志)段,适合演练时缩短等待时间。生产环境要结合写入量和存储成本调整,不建议不评估就照搬。重启后执行一次手动切换,确认归档目录出现新文件。
psql -U postgres -c "SELECT pg_switch_wal();"
sleep 10
ls -lh /backup/pg/wal | tail
做基础备份,建立可回放的起点
时间点恢复需要一个基础备份作为起点,再用 WAL(Write-Ahead Logging,预写日志)向前回放。PostgreSQL 自带的 pg_basebackup(一种物理基础备份工具)可以直接生成可恢复的数据目录副本。演练时建议加 -X stream,让备份过程中产生的 WAL(Write-Ahead Logging,预写日志)也被带上,减少起始缺口。
pg_basebackup -U replication_user -D /backup/pg/base_20260812 -Fp -Xs -P -l "base backup 2026-08-12 drill"

恢复到测试库,并验证 pg_dump 文件
先验证逻辑备份恢复,这能暴露对象权限、扩展、编码和索引重建问题。创建一个新库,不要覆盖原库;恢复完成后跑业务关键查询,确认表数量和核心数据一致。
createdb -U postgres appdb_restore_test
pg_restore -U postgres -d appdb_restore_test --jobs=4 /backup/pg/dump/appdb_20260812.dump 2>/backup/pg/dump/restore_appdb_20260812.log
psql -U postgres -d appdb_restore_test -c "\dt"
psql -U postgres -d appdb_restore_test -c "SELECT count(*) FROM orders;"
如果恢复日志中出现 extension does not exist,先在测试库安装扩展,再重新恢复;如果出现 role does not exist,先导入 roles.sql 或创建同名角色。不要为了让恢复“看起来成功”而忽略 owner 和权限错误,因为应用连接数据库时可能正好依赖这些权限。
模拟误操作,用 WAL 恢复到指定时间点
接下来演练时间点恢复。先在测试环境记录一个恢复目标时间,再制造一条可识别的数据变化。例如插入一条演练记录,随后删除它。目标是把数据库恢复到删除之前、插入之后的时间点。
SELECT now();
INSERT INTO drill_events(event_name, created_at) VALUES ('restore_drill_marker', now());
SELECT now();
DELETE FROM drill_events WHERE event_name = 'restore_drill_marker';
SELECT now();
假设插入后的时间为 2026-08-12 10:04:55+08,恢复时就把 recovery_target_time 设置到这个时间附近。先停止测试实例,清空目标恢复目录,再复制基础备份。
rm -rf /restore/pgdata/*
cp -a /backup/pg/base_20260812/* /restore/pgdata/
chown -R postgres:postgres /restore/pgdata
chmod 700 /restore/pgdata
在 PostgreSQL 12 及以上版本中,可以通过 postgresql.auto.conf 或配置文件设置恢复参数,并创建 recovery.signal。下面示例把 WAL(Write-Ahead Logging,预写日志)从归档目录取回,并在目标时间暂停,方便人工检查。
restore_command = 'cp /backup/pg/wal/%f %p'
recovery_target_time = '2026-08-12 10:04:55+08'
recovery_target_action = 'pause'
touch /restore/pgdata/recovery.signal
sudo -u postgres pg_ctl -D /restore/pgdata start
psql -U postgres -c "SELECT pg_is_wal_replay_paused();"
恢复暂停后,查询演练标记是否存在。如果标记存在,说明已经恢复到删除之前;如果不存在,要核对目标时间是否选错、WAL(Write-Ahead Logging,预写日志)是否缺段、时区是否一致。这个验证点必须写进演练记录,否则无法证明恢复点真的有效。

把演练结果量化:RPO、RTO 和失败原因
一次合格的 PostgreSQL 备份恢复演练,最后要沉淀成可量化记录。RPO(Recovery Point Objective,恢复点目标)回答“最多丢多少数据”,RTO(Recovery Time Objective,恢复时间目标)回答“多久恢复服务”。例如本次演练可以记录:pg_dump(一种 PostgreSQL 逻辑备份工具)导出耗时 8 分钟,逻辑恢复耗时 14 分钟;WAL(Write-Ahead Logging,预写日志)时间点恢复耗时 11 分钟,恢复点误差控制在 1 分钟内。
- 备份文件校验:记录 dump 文件大小、对象清单行数、
pg_restore -l输出是否包含关键 schema。 - WAL(Write-Ahead Logging,预写日志)连续性:记录归档目录最后 10 个文件、最近一次
pg_switch_wal()是否成功。 - 恢复验证:记录 3 到 5 条关键 SQL 的恢复前后结果,例如订单数、最近一笔订单 ID、配置表版本号。
- 耗时指标:分别记录导出、复制、恢复、应用验证耗时,避免只写“恢复成功”。
如果测试环境跑在 VPS(虚拟专用服务器)主机 或[独立服务器](https://cn.hostease.com/dedicated-server/)上,也要把磁盘吞吐、CPU(中央处理器)和内存占用写进记录。恢复速度往往受磁盘 I/O(输入输出)影响,比单纯的数据库参数更明显;如果还要排查查询性能,可参考 MySQL 慢查询日志分析实战 的记录方法。Hostease 在主机与服务器方案中提供不同资源规格,实际选择仍应以数据库大小、恢复窗口和预算为依据,不要只看单项配置。
常见问题:为什么备份有了,恢复还是失败
第一个常见问题是角色和权限缺失。pg_dump(一种 PostgreSQL 逻辑备份工具)备份的是数据库对象,不一定包含集群级角色。解决方法是在演练流程中加入 pg_dumpall --roles-only,并在恢复前导入。第二个问题是 WAL(Write-Ahead Logging,预写日志)归档不连续。只要缺少中间某个段,时间点恢复就可能停在半路,所以归档目录需要监控磁盘空间和文件数量。
建议的执行频率与落地方式
总结来看,PostgreSQL 备份恢复演练应至少按月执行一次;如果业务每天都有订单或会员数据变化,建议在重大版本发布前额外做一次。推荐把演练分成三档:每周检查 pg_dump(一种 PostgreSQL 逻辑备份工具)文件和对象清单;每月做一次测试库恢复;每季度做一次完整 WAL(Write-Ahead Logging,预写日志)时间点恢复,并把 RPO(Recovery Point Objective,恢复点目标)和 RTO(Recovery Time Objective,恢复时间目标)写入运维文档。
如果你需要为企业网站、外贸站或会员系统建立更稳妥的数据库灾备流程,可以考虑从“备份文件存在”升级到“恢复流程可验证”。先用本文命令完成一轮演练,再根据数据库大小、业务低峰期和服务器资源调整频率。真正可靠的备份,不是存放在某个目录里的文件,而是在事故发生前已经被恢复、核对并记录过的方案。