PostgreSQL 备份到底能不能恢复:一个 60 行的演练脚本(pg_dump → 临时库 → 逐表比对)
下载可直接运行的 bash 脚本:pg_dump -Fc 备份、恢复到临时库、逐表 count(*) 比对,输出 CSV 证据后自动删除临时库。附 PostgreSQL 17 实测结果。
“有备份”不等于“能恢复”。这个脚本把一次恢复演练压缩成一条命令,并留下备份大小、备份耗时、恢复耗时、逐表行数比对四类证据。
适用对象
- 演练的产出是 summary.csv 和 compare.csv,可以直接作为恢复演练记录归档。
- 脚本只读源库,但会在同一实例创建并删除一个临时库,需要 CREATEDB 权限和足够磁盘。
- 逐表 count(*) 验证的是“数据都回来了”,不验证业务正确性;关键表建议再补抽样校验。
- pg_dump 是逻辑备份,不能替代基于 WAL 的时间点恢复(PITR)。
一条命令怎么跑
连接参数沿用 libpq 环境变量:`PGHOST=10.0.0.5 PGUSER=dba ./pg-backup-restore-drill-v1.0.sh shop /data/drill/2026-10-15`。第二个参数是证据输出目录,不填就在当前目录建 drill-时间戳。密码建议放在 ~/.pgpass,不要写进命令行。
建议在从库或者一个专门的演练实例上跑:恢复会占用和源库差不多的磁盘,count(*) 在大表上会扫全表。脚本用 `trap` 保证无论成功失败都会删除临时库,临时库名是 `<源库>_restore_drill_<时间戳>`,不会和现有库重名。
测试库上的输出
summary.csv 记录了 dump_bytes=1222006(约 1.2MB)、dump_seconds=0、restore_seconds=1、tables_compared=2、tables_diff=0、result=PASS。compare.csv 两行都是 OK:public.orders 200000/200000,public.users 1000/1000。
测试库太小,耗时没有参考意义。在你自己的库上跑出来的 restore_seconds 才是关键数字:拿它和业务能接受的停机时间(RTO)比,如果恢复要 3 小时而 RTO 是 1 小时,那就说明需要换成物理备份 + WAL 归档,或者常备一个热备从库。
| 文件 | 内容 | 用途 |
|---|---|---|
| summary.csv | 备份大小、备份/恢复秒数、比对表数、差异表数、PASS/FAIL | 演练记录、RTO 对比 |
| compare.csv | 每张表的源库行数、恢复后行数、是否一致 | 定位缺失表或行数差异 |
| src_counts.csv / restored_counts.csv | 两侧原始行数 | 复核 |
| <源库>.dump | 本次备份文件 | 可保留作为一份已验证可恢复的备份 |
我实际遇到过的几种 FAIL
脚本只告诉你“不一致”,原因要人看。常见的几类:
本节判断
- 源库在备份期间持续写入:pg_dump 拿的是开始那一刻的快照,之后再 count(*) 自然比备份多。要么在从库/低峰跑,要么把差异小的高频写入表单独标注。
- 扩展缺失:源库用了 PostGIS、pg_trgm 等扩展,临时库所在实例没装,pg_restore 会因 --exit-on-error 直接失败——这恰恰说明换机器恢复会失败。
- 角色缺失:脚本加了 --no-owner 绕过属主问题,但 GRANT 仍可能报错;真实恢复前要先导出角色(pg_dumpall --roles-only)。
- 大对象和 UNLOGGED 表:UNLOGGED 表的数据本来就不保证;大对象需要确认 pg_dump 参数是否包含。
多久演练一次
我的建议是每月一次固定演练,加上每次大版本升级、迁移或备份策略变更后各一次。把 summary.csv 按月份归档,半年后你就有一条“恢复耗时随数据量增长”的曲线,什么时候该换备份方案一目了然。
参考依据
以下来源用于确认市场趋势、政策背景和术语边界;具体落地方案仍以客户的数据范围、权限和交付目标为准。
常见问题
可以在生产主库上直接跑吗?
技术上可以,脚本对源库只读,但备份和 count(*) 都会占 IO,并且会在同一实例创建临时库。建议从库或低峰执行,并先确认磁盘余量至少等于源库大小。
我们用的是物理备份(pgBackRest / pg_basebackup),这个脚本有用吗?
思路一样,但恢复步骤不同:物理备份要恢复成一个独立实例再比对。比对部分(count_rows 函数)可以直接复用。
count(*) 一致就说明恢复成功了吗?
只能说明行都回来了。建议对核心表再加一项抽样比对,例如按主键取最近 1000 行做 md5 校验。