Skip to content

pg_restore 恢复 ​

pg_dump 有四种输出格式,其中三种(custom、directory、tar)是归档格式——它们不是可以直接喂给 psql 的 SQL 脚本,而是带有「对象目录(TOC)」的结构化容器。恢复这类归档必须用 pg_restore。

pg_restore 的价值远不止「把归档还原成库」。它有两种工作模式:直接连库恢复(-d dbname)和生成 SQL 脚本(不指定 -d,输出到文件或标准输出)。前者支持并行恢复(-j)、单事务恢复、按 section 分阶段恢复;后者让你在真正执行之前完整审阅即将运行的 SQL,或者把归档转成纯文本以便人工修改。

更关键的是「选择性恢复」:归档里记录了每一项对象(表、索引、约束、数据、大对象……)的条目,pg_restore -l 可以列出这份清单,导出成文本、编辑、再用 -L 按编辑后的清单与顺序恢复。这是「只找回一张表」「跳过某个索引」「调整恢复顺序」这类需求的实现方式。

前置知识

建议先阅读:pg_dumpall 全集群备份、pg_dump 基础用法

基础概念 ​

pg_restore 的适用边界 ​

归档格式用 pg_restore 恢复用 psql 恢复
plain(-Fp)✗ 不支持✔ 唯一方式
custom(-Fc)✔✗
directory(-Fd)✔✗
tar(-Ft)✔✗

判断依据很简单:归档格式是二进制 / 结构化的,必须由 pg_restore 解释。反过来,pg_dumpall 的输出永远是纯文本,只能用 psql。

两种工作模式 ​

bash
# 模式一:直接恢复到数据库
pg_restore -h host -U user -d mydb /backup/mydb.dump

# 模式二:生成 SQL 脚本(不指定 -d)
pg_restore -f mydb.sql /backup/mydb.dump

两种模式的能力差异很大:

能力直接连库(-d)生成脚本(无 -d)
并行 -j✔✗(被忽略)
单事务 -1✔无意义
--disable-triggers✔✔(输出到脚本)
--no-data-for-failed-tables✔✗
恢复失败时的错误统计✔不适用
人工审阅 / 修改 SQL✗✔

生成脚本模式是审阅不可信归档的唯一手段,官方安全警告中明确提到这一点:先用 --file 把归档转成文本,检查过再执行。

TOC:归档的目录清单 ​

每个归档内部都有一份「目录表(Table of Contents)」,记录每一项待恢复对象的类型、OID、所属、名称、属主。pg_restore -l 就是把这份清单打印出来:

text
;
; Archive created at 2026-09-30 10:12:33 CST
;     dbname: orders
;     TOC Entries: 42
;     Compression: zstd
;     Dump Version: 1.15-0
;     Format: CUSTOM
;     Integer: 4 bytes
;     Offset: 8 bytes
;     Dumped from database version: 18.0
;     Dumped by pg_dump version: 18.0
;
;
; Selected TOC Entries:
;
215; 1259 16385 TABLE public orders postgres
216; 1259 16386 TABLE public order_items postgres
217; 1259 16387 SEQUENCE public orders_id_seq postgres
215; 1259 16388 INDEX public orders_pkey postgres
216; 1259 16389 CONSTRAINT public orders_pkey postgres
3371; 0 16385 TABLE DATA public orders postgres
3372; 0 16386 TABLE DATA public order_items postgres
3373; 0 16387 SEQUENCE SET public orders_id_seq postgres
213; 1259 16390 TRIGGER public trg_orders_audit postgres

每一行前面那串数字是「dumpId; 表 OID; 对象 OID」的固定结构,后面是对象类型、schema、名称、属主。这份输出有两个用途:

  1. -l 输出可直接作为 -L 的输入,实现「编辑后按改动的清单恢复」;
  2. -l 也可以用来判断归档是否完整可读,是备份后最廉价的校验手段。

恢复阶段:pre-data / data / post-data ​

归档中的对象被划分为三个阶段,pg_restore 默认按此顺序恢复:

阶段内容为什么在这一阶段
pre-data表定义、序列定义、类型、函数、schema、扩展必须先有容器
data表数据、大对象、序列值(setval)需要表已存在
post-data索引、约束(主键 / 外键 / 检查)、触发器、规则先灌数据再建索引更快,外键也才不会被逐行校验

理解这个顺序是排查恢复问题的关键。例如「恢复后外键不存在」往往不是没导,而是 post-data 阶段某条语句失败被跳过了。

为什么并行恢复需要目录格式 ​

-j 会把最耗时的三类步骤——装载数据、创建索引、创建约束——分给多个会话并发执行。并发的前提是各任务之间没有顺序依赖。

  • custom 格式:单个文件,但内部通过 seek 定位各对象,多个 worker 可以访问同一文件的不同偏移,因此支持 -j;
  • directory 格式:天然每表一个文件,更适合并发;
  • tar 格式:单个顺序文件流,无法并行,也不支持 -j;
  • 输入必须是常规文件或目录,不能是管道或标准输入。

关键术语 ​

术语含义
归档(archive)pg_dump 的 -Fc / -Fd / -Ft 输出,需 pg_restore 读取
TOC归档内的对象目录清单,-l 打印、-L 消费
-l / -L列出清单 / 按指定清单恢复
sectionpre-data / data / post-data 三个阶段
--role恢复期间执行 SET ROLE 切换身份
依赖对象表所依赖的序列、类型、索引、约束、触发器
--strict-names过滤模式必须匹配到对象,否则报错
row_securityPG18 中 pg_restore 默认置为 off 以完整写入数据

语法与用法 ​

基本语法 ​

bash
pg_restore [connection-option...] [option...] [filename]

filename 是归档文件(或目录格式的目录)。省略时从标准输入读取。

最简示例 ​

bash
# 恢复到已有数据库
pg_restore -h 10.0.0.5 -U postgres -d orders /backup/orders.dump

# 并行恢复(需归档为 custom / directory 格式)
pg_restore -h 10.0.0.5 -U postgres -d orders -j 8 /backup/orders.dump

# 重建对象(先 DROP 再 CREATE)
pg_restore -h 10.0.0.5 -U postgres -d orders --clean --if-exists /backup/orders.dump

# 由归档创建数据库
pg_restore -h 10.0.0.5 -U postgres -d postgres --create /backup/orders.dump

# 转成 SQL 脚本以便审阅
pg_restore -f /tmp/orders.sql /backup/orders.dump

查看与选择清单 ​

参数长选项含义
-l--list列出归档的 TOC
-L--use-list只恢复清单文件中列出的项,并按其中的顺序
-f--file输出到文件(配合 -l 时为清单输出到文件)
-v--verbose输出详细进度
bash
# 清单导出到文件,便于编辑
pg_restore -l /backup/orders.dump > /tmp/toc.txt

# 按编辑后的清单恢复(只保留需要的行)
pg_restore -L /tmp/toc.txt -d orders /backup/orders.dump

对象过滤 ​

参数长选项含义
-t--table只恢复指定表(不支持通配符、不能带 schema 名)
-n--schema只恢复指定 schema 中的对象
-N--exclude-schema排除指定 schema
-I--index只恢复指定索引
-P--function只恢复指定函数(须写明参数类型)
-T--trigger只恢复指定触发器
--filter—从文件读取 include / exclude 规则
bash
# 只恢复一张表(表名不含 schema;需配 -n 限定 schema)
pg_restore -n public -t orders -d orders /backup/orders.dump

# 只恢复某个 schema
pg_restore -n app -d orders /backup/orders.dump

# 排除审计 schema
pg_restore -N audit -d orders /backup/orders.dump

# 只恢复一个索引定义
pg_restore -I orders_pkey -d orders /backup/orders.dump

pg_restore -t 与 pg_dump -t 的行为不一致,务必注意三点差异:

  1. pg_restore -t 不支持通配符;
  2. pg_restore -t 不能包含 schema 名,要限定 schema 需另加 -n;
  3. pg_restore -t 不会带上附属对象(索引、约束、触发器),而 pg_dump -t 会。

也就是说:pg_restore -t orders 恢复出来的是「表定义 + 数据」,索引和主键不会跟着回来。要拿到完整表,用 -L 清单方式更可靠。

内容选择 ​

参数长选项含义
-a--data-only只恢复数据(含表数据、大对象、序列值)
-s--schema-only只恢复定义,不含数据与统计信息
--section—指定阶段:pre-data / data / post-data,可重复
--no-data—不恢复数据
--no-schema—不恢复定义
--no-statistics—不恢复统计信息
--no-comments—不恢复注释
--no-policies—不恢复 RLS 策略
--no-publications / --no-subscriptions—不恢复发布 / 订阅
--no-tablespaces—不指定表空间,全部建在默认表空间
bash
# 先建结构
pg_restore -d orders --section=pre-data /backup/orders.dump
# 再灌数据
pg_restore -d orders --section=data /backup/orders.dump
# 最后建索引与约束
pg_restore -d orders --section=post-data /backup/orders.dump

覆盖与建库 ​

参数长选项含义
-c--clean恢复前先 DROP 将被恢复的对象
--if-exists—DROP 时加 IF EXISTS,须与 --clean 同用
-C--create先创建数据库再恢复进去
-O--no-owner不设置属主,对象归恢复者所有
-x--no-privileges不恢复 GRANT / REVOKE
--role—恢复期间 SET ROLE 到指定角色
bash
# 完整覆盖式恢复
pg_restore -d orders --clean --if-exists --create /backup/orders.dump

# 跨环境恢复:不带属主与权限
pg_restore -d orders --no-owner --no-privileges /backup/orders.dump

# 以指定角色身份恢复(连接用户需是该角色的成员)
pg_restore -d orders --role=app_owner /backup/orders.dump

-C 的一个细节:使用 --create 时,-d 指定的库仅用于执行最初的 DROP DATABASE 与 CREATE DATABASE,实际数据会被恢复到归档中记录的数据库名里。所以 -d postgres + --create 是标准写法。

事务与错误控制 ​

参数长选项含义
-1--single-transaction整个恢复包在一个事务内,隐含 --exit-on-error
-e--exit-on-error遇到错误立即退出,默认是继续并最后统计错误数
--disable-triggers—数据装载期间临时禁用触发器(需超级用户)
--no-data-for-failed-tables—建表失败的表的数据不再恢复
--enable-row-security—打开 row_security 而不是默认的关闭
bash
# 全有或全无的恢复
pg_restore -d orders --single-transaction /backup/orders.dump

# 数据恢复期间禁用触发器(避免逐行触发业务逻辑)
pg_restore -d orders --disable-triggers --section=data /backup/orders.dump

# 目标库已有扩展辅助表,跳过建表失败的表的数据
pg_restore -d orders --no-data-for-failed-tables /backup/orders.dump

详细说明 ​

清单编辑流程:-l → 编辑 → -L ​

这是 pg_restore 最核心的工作流,也是其他工具无法替代的能力。

第一步:导出清单

bash
pg_restore -l /backup/orders.dump > /tmp/toc.txt
text
;
; Archive created at 2026-09-30 10:12:33 CST
;     dbname: orders
;     TOC Entries: 42
;     Compression: zstd
;     Format: CUSTOM
;
; Selected TOC Entries:
;
215; 1259 16385 TABLE public orders postgres
216; 1259 16386 TABLE public order_items postgres
217; 1259 16387 SEQUENCE public orders_id_seq postgres
215; 1259 16388 INDEX public orders_pkey postgres
3371; 0 16385 TABLE DATA public orders postgres
3372; 0 16386 TABLE DATA public order_items postgres
3373; 0 16387 SEQUENCE SET public orders_id_seq postgres

第二步:编辑清单

保留所需要的行;不需要的行可以删除,也可以在行首加 ; 注释掉。恢复顺序完全由文件中的行序决定,因此还可以通过移动行来调整顺序(例如把某个索引提前建)。

text
; 只恢复订单表本身,跳过明细表
215; 1259 16385 TABLE public orders postgres
3371; 0 16385 TABLE DATA public orders postgres
; 这行被注释掉,不会恢复
;3372; 0 16386 TABLE DATA public order_items postgres

第三步:按清单恢复

bash
pg_restore -L /tmp/toc.txt -d orders /backup/orders.dump

要点:

  • -L 可以与 -n / -t 等过滤参数同时使用,此时过滤条件会进一步收窄清单;
  • 清单文件里的行必须保持原格式(开头的 dumpId; 表OID 对象OID 不能改);
  • 这是实现「带索引的单表恢复」的正确方式——-t 拿不到索引,-L 可以。

并行恢复 -j 的使用条件 ​

bash
pg_restore -d orders -j 8 /backup/orders.dump

四条硬性约束:

  1. 仅 custom 与 directory 格式支持;tar 格式不支持;
  2. 输入必须是常规文件或目录,不能是管道或标准输入(并行需要随机访问);
  3. 不能与 --single-transaction 同时使用——两者目标冲突,一个要并发、一个要原子;
  4. 每个 job 是独立进程或线程,各占一条数据库连接,注意 max_connections。

并发值怎么选:官方建议从服务器 CPU 核数起步,很多场景下超过核数反而更快,但过高会因为资源争抢导致性能下降(thrashing)。实际调优时应该用真实数据做几轮压测。

需要注意 -j 只加速三类步骤:装载数据、创建索引、创建约束。pre-data 阶段的对象创建(表、类型、函数)是串行的,因此如果归档里结构极多而数据很少,-j 收益有限。

恢复数据的顺序与约束 ​

这是理解「为什么有些恢复会失败」的关键。

data 阶段默认使用 COPY 装载。如果表上有外键约束,逐行 COPY 会因为「引用的行还不存在」而失败。PostgreSQL 的解决方式是阶段划分:

text
pre-data : CREATE TABLE(无索引、无外键)
data     : COPY 装载数据(此时表上没有外键,不会失败)
post-data: CREATE INDEX + ADD CONSTRAINT(一次性校验,比逐行快得多)

所以正常的归档恢复不会遇到外键顺序问题——这正是不用 --single-transaction 时也能正确恢复的原因。

但如果你用 --section=data 手动只恢复数据阶段,就需要确保表与约束已经是最终状态,此时可能踩到外键与唯一约束问题。这时 --disable-triggers 才有意义。

--disable-triggers 的用途与代价 ​

bash
pg_restore -d orders --section=data --disable-triggers /backup/orders.dump

它让恢复过程临时关闭目标表上的触发器(外键约束的检查也通过触发器实现),避免在数据装载阶段触发业务逻辑或约束校验。

三个约束条件必须清楚:

  1. 只在「不带 schema 的恢复」中有意义(即 --section=data 或 --data-only)。因为默认恢复下外键本来就在 post-data 阶段才创建;
  2. 执行该命令需要超级用户权限(或通过 -S 指定超级用户名);
  3. 它绕过的是触发器层面的检查,不做数据正确性校验,恢复后可能出现违反约束的数据。

一个典型用法:把数据先「硬灌」进库,等数据齐了再统一加约束,这样单行顺序问题就不会中断整个恢复。

--single-transaction 的取舍 ​

bash
pg_restore -d orders --single-transaction /backup/orders.dump

收益:要么全部成功,要么全部回滚。目标库不会留下「恢复了一半」的状态。

代价:非常多,务必权衡:

  • 整个恢复期间持有一个长事务,会阻止 VACUUM 回收,可能造成膨胀;
  • 隐含 --exit-on-error,任何一条语句失败就整体回滚,而默认模式下几百条「对象已存在」的无害错误不会影响结果;
  • 不能与 -j 并用,失去并行加速;
  • CREATE INDEX 会在事务内执行(非 CONCURRENTLY),恢复时间可能更长;
  • 某些语句本身不能在事务块内运行(如 CREATE TABLESPACE、CREATE DATABASE),如果归档中含这类对象会失败。

实践建议:默认不要用 --single-transaction,改用「恢复到临时库 → 校验 → 切换」的两阶段方式获得原子性,同时保留并行能力。

bash
# 推荐:恢复到临时库,校验通过后再改名切换
createdb orders_new
pg_restore -d orders_new -j 8 --no-owner /backup/orders.dump
psql -c 'ALTER DATABASE orders RENAME TO orders_old;'
psql -c 'ALTER DATABASE orders_new RENAME TO orders;'

常见恢复错误与处理 ​

依赖对象不存在

表现:function xxx() does not exist、type xxx does not exist、operator does not exist。

根因:用了 -t / -n 做局部恢复,但依赖的类型、函数、扩展没被包含进来。归档本身没问题,是局部恢复的固有局限。

处理:改用 -L 清单方式,把依赖项一起勾选;或先手工创建扩展 / 类型。

属主不存在

text
ERROR: role "app_user" does not exist

处理:目标库没有该角色。两种选择——先建角色,或加 --no-owner。

bash
pg_restore -d orders --no-owner --no-privileges /backup/orders.dump

扩展不存在

text
ERROR: extension "postgis" is not available

处理:目标机没装扩展。先安装扩展包,或在源端用 -e 排除特定扩展,或恢复时忽略该错误(默认模式下会继续)。

对象已存在导致的刷屏错误

text
pg_restore: error: could not execute query: ERROR: relation "orders" already exists

处理:这是「恢复到一个已有内容的库」的常见现象。默认模式下属于可忽略错误(结尾会统计错误数)。要清理干净,加 --clean --if-exists。

并行恢复被中止

text
pg_restore: error: could not find block containing chunk ...

或数据加载时出现 unexpected end of file。多见于归档不完整、被截断,或使用了管道。处理:验证归档完整性(pg_restore -l),确认输入是常规文件。

--filter 文件格式 ​

bash
pg_restore -d orders --filter=filter.txt /backup/orders.dump

文件格式固定为「动作 + 对象类型 + 模式」:

text
# 只恢复 app schema 与 orders 表
include schema app
include table public.orders

# 排除审计相关的 schema
exclude schema audit

规则要点:

  • 动作只有 include / exclude 两种;
  • 对象类型有 function / index / schema / table / trigger;
  • function / index / table / trigger 只能与 include 搭配;只有 schema 支持 exclude;
  • # 开头的行是注释,空行忽略;行尾也可以追加注释;
  • --filter 可与其他过滤参数并用,也可多次指定;文件名写 - 表示从标准输入读。

row_security 与恢复完整性 ​

这一点直接关系到「备份数据是否被静默丢行」。

pg_restore 默认会把 row_security 设为 off,目的是确保所有数据都被写进表里——即使恢复账号不是超级用户、也没有 BYPASSRLS,切换为 off 后 PostgreSQL 采取的策略是「宁可报错,也不静默漏行」。

bash
# 默认行为:row_security = off,保证完整写入
pg_restore -d orders /backup/orders.dump

如果确实想按 RLS 策略过滤后再恢复:

bash
# 打开 row_security(注意:dump 必须是 INSERT 格式,COPY FROM 不支持 RLS)
pg_restore -d orders --enable-row-security /backup/orders.dump

注意 --enable-row-security 的额外限制:因为 COPY FROM 不支持行级安全,所以归档必须是用 --inserts 或 --column-inserts 生成的。这个选项与备份端 pg_dump 的账号权限设置是同一件事的两面,排查「备份丢行」问题时必须同时看两端。

恢复后必做的收尾 ​

恢复完成后有三件事不能忘:

bash
# 1) 更新优化器统计信息(数据是 COPY 进来的,统计信息不会自动准确)
psql -d orders -c 'ANALYZE;'

# 2) 确认序列当前值正确(避免主键冲突)
psql -d orders -c 'SELECT last_value, is_called FROM orders_id_seq;'

# 3) 确认约束与索引都建起来了
psql -d orders -c '\d orders'

ANALYZE 尤其重要:恢复的数据没有经过正常的写入路径,pg_statistic 里的信息是旧的甚至不存在,不更新会直接导致恢复后查询计划严重劣化。

实战示例 ​

示例一:标准的整库恢复 ​

bash
#!/usr/bin/env bash
set -euo pipefail

DUMP=/backup/orders.dump
NEWDB=orders_restore

# 1) 校验归档可读
pg_restore -l "$DUMP" > /dev/null
echo "archive ok"

# 2) 建目标库(建议 template0 避免继承 template1 中的对象)
psql -h 10.0.0.5 -U postgres -c "CREATE DATABASE $NEWDB TEMPLATE template0;"

# 3) 并行恢复,不带属主与权限
pg_restore \
  -h 10.0.0.5 -U postgres \
  -d "$NEWDB" \
  -j 8 \
  --no-owner --no-privileges \
  --verbose \
  "$DUMP" 2> /tmp/restore.log

# 4) 收尾
psql -h 10.0.0.5 -U postgres -d "$NEWDB" -c 'ANALYZE;'
echo "restore ok"

示例二:只找回一张表及其索引与约束 ​

bash
# 1) 导出清单
pg_restore -l /backup/orders.dump > /tmp/toc.txt

# 2) 只看和 orders 表相关的条目,人工确认要勾选哪些
grep -E 'orders($| )|orders_pkey|orders_tenant' /tmp/toc.txt
text
215; 1259 16385 TABLE public orders postgres
215; 1259 16388 INDEX public orders_pkey postgres
215; 1259 16389 CONSTRAINT public orders_pkey postgres
215; 1259 16390 INDEX public orders_tenant_idx postgres
3371; 0 16385 TABLE DATA public orders postgres
3373; 0 16387 SEQUENCE SET public orders_id_seq postgres
bash
# 3) 生成只含这些行的清单
grep -E 'orders($| )|orders_pkey|orders_tenant|TABLE DATA public orders|SEQUENCE SET public orders_id_seq' \
  /tmp/toc.txt > /tmp/only_orders.txt

# 4) 按清单恢复
pg_restore -L /tmp/only_orders.txt -d orders /backup/orders.dump

这个流程比 pg_restore -t orders 更可靠,因为索引与约束都在清单里被显式包含。

示例三:分阶段恢复以便监控耗时 ​

bash
# 阶段一:结构(通常很快)
time pg_restore -d orders --section=pre-data /backup/orders.dump

# 阶段二:数据(最耗时,可并行)
time pg_restore -d orders --section=data -j 8 /backup/orders.dump

# 阶段三:索引与约束(次耗时,可并行)
time pg_restore -d orders --section=post-data -j 8 /backup/orders.dump

分阶段的好处是可以在每个阶段之间插入检查点,也让失败后的重试范围更小——数据阶段失败时不必重跑结构阶段。

示例四:把归档转成 SQL 脚本审阅 ​

bash
# 生成可读脚本
pg_restore --verbose --no-owner -f /tmp/orders_review.sql /backup/orders.dump

# 审阅关键片段
grep -n 'CREATE TABLE' /tmp/orders_review.sql | head -5
grep -c 'COPY ' /tmp/orders_review.sql
grep -n 'ALTER TABLE.*ADD CONSTRAINT' /tmp/orders_review.sql | head -5
text
CREATE TABLE public.orders (
    id bigint NOT NULL,
    tenant_id integer NOT NULL,
    amount numeric(12,2) NOT NULL
);

COPY public.orders (id, tenant_id, amount) FROM stdin;

这是官方推荐的安全做法:不可信来源的归档,先用 -f 转成文本审阅,再执行。归档内容可以在恢复时执行任意 SQL,跳过这一步等于把源端的超级用户权限直接搬到目标端。

示例五:用 filter 文件做精细化恢复 ​

bash
cat > /tmp/restore_filter.txt <<'EOF'
# 只恢复业务 schema 与核心表
include schema app
include table public.orders
include table public.order_items

# 排除审计数据
exclude schema audit
EOF

pg_restore -d orders --filter=/tmp/restore_filter.txt \
  --no-owner -j 4 /backup/orders.dump

示例六:恢复日志中的错误分类统计 ​

bash
pg_restore -d orders /backup/orders.dump 2> /tmp/restore_err.log
text
pg_restore: error: could not execute query: ERROR:  role "app_user" does not exist
Command was: ALTER TABLE public.orders OWNER TO app_user;
pg_restore: error: could not execute query: ERROR:  role "app_user" does not exist
Command was: ALTER TABLE public.order_items OWNER TO app_user;
pg_restore: warning: errors ignored on restore: 12
bash
# 按错误类型归类,判断是「可忽略」还是「必须处理」
grep 'ERROR:' /tmp/restore_err.log | sed 's/.*ERROR:  //' | sort | uniq -c | sort -rn
text
      8 role "app_user" does not exist
      4 relation "orders" already exists

role does not exist 是属主问题(加 --no-owner 即可消除);already exists 是目标库已有内容(加 --clean --if-exists 或改用空库)。两类都不是数据丢失,属于环境问题。

注意事项 ​

pg_restore 只能读取归档格式

bash
# 错误:plain 格式必须用 psql
pg_restore -d orders /backup/orders.sql
# 正确
psql -d orders -f /backup/orders.sql

pg_restore -t 与 pg_dump -t 行为不同

bash
# 这条只恢复表定义与数据,索引与约束不会回来
pg_restore -n public -t orders -d orders /backup/orders.dump
# 要拿到完整对象,用 -L 清单方式

-t 不支持通配符、不能带 schema 名

bash
# 两条都会失败
pg_restore -t 'public.orders_*' -d orders /backup/orders.dump
pg_restore -t 'public.orders' -d orders /backup/orders.dump
# 正确写法:schema 与表名分开
pg_restore -n public -t orders -d orders /backup/orders.dump

--if-exists 必须与 --clean 同用

bash
# 无效组合(--if-exists 被忽略)
pg_restore -d orders --if-exists /backup/orders.dump
# 正确
pg_restore -d orders --clean --if-exists /backup/orders.dump

-j 不能与 --single-transaction 同用

bash
# 这个组合会报错
pg_restore -d orders -j 8 --single-transaction /backup/orders.dump

-j 不能从管道或 stdin 读取

bash
# 错误:并行需要随机访问
cat /backup/orders.dump | pg_restore -d orders -j 8
# 正确:直接给文件路径
pg_restore -d orders -j 8 /backup/orders.dump

--clean 会先删对象,误操作代价高

bash
# 危险:目标库里的同名对象会被删掉再重建,期间数据不可用
pg_restore -d production --clean --if-exists /backup/orders.dump

在恢复生产库之前,务必确认目标库名正确。--clean 删除的是「归档中存在的对象」,不会删归档外的表,但这也意味着不会清理干净——残留对象可能造成困惑。

-C 恢复的库名来自归档,不是 -d

bash
# 数据会恢复到归档中记录的 "orders" 库,而不是 mytemp
pg_restore -d mytemp --create /backup/orders.dump

--disable-triggers 需要超级用户

bash
# 普通用户执行会失败
pg_restore -d orders --section=data --disable-triggers /backup/orders.dump
# 需要 -S 指定超级用户,或以超级用户运行
pg_restore -d orders -S postgres --section=data --disable-triggers /backup/orders.dump

--no-data-for-failed-tables 仅在直接连库时有效

生成 SQL 脚本时不生效,因为脚本模式下不知道建表是否会失败。

恢复后必须 ANALYZE

不更新统计信息会让恢复后的查询计划严重劣化,某些场景下性能下降一个数量级:

sql
ANALYZE;

序列值不会自动跟随数据修正

归档中的 SEQUENCE SET 是备份时刻的值。若在「备份 → 恢复」之间又有数据写入并产生了更大的 ID,恢复后可能发生主键冲突。恢复后应校验:

sql
SELECT last_value FROM orders_id_seq;
SELECT max(id) FROM orders;

恢复脚本会执行源端超级用户写入的任意代码

这是官方的明确安全警告。不可信归档必须先用 -f 转成文本审阅。

最佳实践 ​

  1. 默认不用 --single-transaction,改用「恢复到临时库 + 校验 + 切换」获得原子性,从而保留 -j 并行能力。
bash
createdb orders_new && pg_restore -d orders_new -j 8 /backup/orders.dump
  1. 并行度从服务器 CPU 核数起步,再用真实数据压测调整。核数只是一个安全的起点,过高会因资源争抢反而变慢。

  2. 跨环境恢复一律加 --no-owner --no-privileges,消除「角色不存在」这类环境相关的报错噪音,让真正的错误显形。

  3. 单表恢复用 -L 清单,不要用 -t,否则索引与约束会被漏掉,恢复出来的表「看起来对、用起来慢」。

bash
pg_restore -l /backup/orders.dump > /tmp/toc.txt
pg_restore -L /tmp/toc.txt -d orders /backup/orders.dump
  1. 恢复前先校验归档可读。这是最廉价的前置检查,能挡掉「备份文件本身就是坏的」这类最糟糕的情况:
bash
pg_restore -l "$DUMP" > /dev/null || { echo "corrupt"; exit 1; }
  1. 把恢复日志留档并按错误类型归类,区分「环境问题」与「数据问题」。加 -v 得到详细进度,便于定位卡点。

  2. 恢复后立即执行 ANALYZE,这是恢复流程的必备步骤而不是可选项。

  3. 恢复后校验关键不变量:表行数、序列当前值、约束是否齐全、索引是否建立。

sql
SELECT count(*) FROM orders;
SELECT last_value, is_called FROM orders_id_seq;
bash
psql -d orders -c '\d orders'
  1. 大库恢复优先用 directory 格式归档,它同时支持并行导出与并行恢复,也便于逐文件处理与增量传输。

  2. 恢复耗时纳入演练指标。定期测量「从归档恢复到可用状态」的总时长,验证它是否满足恢复时间目标;RTO 超标时应转向物理备份方案。

  3. 不可信归档先转文本审阅再执行,把「恢复即执行任意代码」这一风险显式管控起来。

bash
pg_restore -f /tmp/review.sql "$DUMP" && less /tmp/review.sql
  1. 生产恢复一律在临时库进行,验证通过后再切换。避免 --clean 直接作用于生产库造成不可逆的窗口期。

下一节

继续学习:自定义与目录格式

参考链接 ​