4. 逻辑备份恢复工具pg_restore与psql

pg_dump 负责“导出”,而真正把逻辑备份还原回数据库,主要依赖两个工具:psql 和 pg_restore。二者分工的关键在于备份格式:psql 只能处理纯文本(plain)格式的 SQL 脚本,它本质上是把脚本逐条发送给服务器执行;pg_restore 则专门处理自定义(custom)、目录(directory)和 tar 格式,这些格式带有对象目录、依赖关系和元数据,因此 pg_restore 能够实现选择性恢复、并行恢复和重排执行顺序。理解这一点,是掌握恢复选项的前提。

一、两种恢复路径与核心选项

纯文本备份的恢复最直接:

psql -h 127.0.0.1 -U app -d appdb -v ON_ERROR_STOP=1 -f appdb.sql

这里 -v ON_ERROR_STOP=1 非常关键。默认情况下 psql 遇到错误会继续往下执行,导致“部分成功”的假象;加上它之后,一旦出错立即中止,便于定位问题。若脚本中包含 CREATE DATABASE,通常需要先连到 postgres 库执行,或使用 -d postgres。

自定义格式备份的恢复则交给 pg_restore:

pg_restore -h 127.0.0.1 -U app -d appdb -j 4 -v appdb.dump

常用选项可归纳为几类:-d 指定目标库;-j 指定并行度;-c 在恢复前先 DROP 对象;--clean --if-exists 组合可避免对象不存在时报错;-n 按 schema 过滤;-t 按表过滤;-a 只恢复数据不建对象;-s 只恢复结构不导数据;-L 使用列表文件精确控制恢复哪些对象。这些选项决定了恢复的粒度与行为。

逻辑备份恢复路径与 pg_restore 处理链 备份格式 plain / custom / dir / tar psql 仅 plain 格式 逐条执行 SQL ON_ERROR_STOP=1 pg_restore custom / dir / tar 对象目录 + 依赖排序 支持并行 / 过滤 目标数据库 appdb 目标数据库 appdb pg_restore -j 4 并行恢复:4 个 worker 按对象依赖分派 Worker 1 表 A 数据 Worker 2 表 B 数据 Worker 3 索引构建 Worker 4 约束 / 触发器 依赖顺序:先建表结构 → 再导数据 → 后建索引/约束;并行仅对数据与索引阶段有效,结构阶段仍串行。 注意:-j 并行度受 CPU、磁盘 IO 与目标库 max_worker_processes 限制,并非越大越快。

二、对象过滤与选择性恢复

生产环境中最常见的需求不是“全库恢复”,而是“只恢复某张表”或“只恢复某个 schema”。pg_restore 提供了多种过滤方式。-n public 只恢复 public schema;-t orders 只恢复 orders 表;-s 只恢复结构;-a 只恢复数据。需要注意的是,-t 过滤时,如果表有外键依赖,可能因依赖对象未恢复而失败,此时可配合 --disable-triggers(需超级用户)或先恢复结构再单独导数据。

更精细的控制方式是列表文件。先用 pg_restore -l appdb.dump > list.txt 导出对象清单,编辑后删除不需要的行,再用 pg_restore -L list.txt -d appdb appdb.dump 恢复。列表文件每行以分号开头的是注释,真正生效的是对象类型、schema、名称和 OID 字段。这种方式适合“恢复 90% 对象、排除少数问题表”的场景。

三、冲突处理与常见错误

恢复时最常见的冲突是“对象已存在”。典型报错:

ERROR:  relation "orders" already exists
ERROR:  role "app" does not exist
ERROR:  permission denied for schema public

第一种情况可用 --clean --if-exists 先删除再重建,或改用 -c。第二种是角色缺失,需先在目标库创建对应角色,或使用 --no-owner 忽略属主设置。第三种是权限问题,通常因为执行恢复的用户不是超级用户,且目标 schema 的 CREATE 权限不足,可先用超级用户执行或调整授权。

另一个高频问题是 ON_ERROR_STOP 与 pg_restore 的差异:pg_restore 默认遇到错误会继续,最后以非零退出码汇总。因此恢复后必须检查退出码和日志中的 ERROR 数量,不能只看“命令跑完了”。

四、数据校验与结果判断

恢复完成后,至少要做三类校验:一是对象数量,对比源库与目标库的表、索引、序列数量;二是行数,对关键表执行 SELECT count(*) 与源库比对;三是抽样数据,检查主键、外键和关键字段是否完整。示例:

-- 源库与目标库分别执行
SELECT schemaname, relname, n_live_tup
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC
LIMIT 20;

若行数差异较大,需检查是否因并行恢复中断、过滤条件误删对象,或数据本身在导出后被修改。对于金额、库存等敏感字段,还应做聚合校验,例如 SUM(amount) 对比,避免“行数对但内容错”的隐蔽问题。

排查顺序建议:先看退出码与 ERROR 日志 → 再核对对象清单 → 再比对行数与聚合值 → 最后抽样验证业务字段。多数恢复失败都能在前两步定位。

注意:并行恢复 -j 只对数据加载和索引构建阶段有效,结构创建阶段仍是串行。并行度过高会加剧锁竞争和 IO 压力,建议从 2~4 起步,观察目标库负载后再调整。

掌握 psql 与 pg_restore 的分工、过滤选项、冲突处理和校验方法,才能把“备份能恢复”从口号变成可验证的能力。恢复不是终点,校验通过才是。