Skip to content

pg_dumpall 全集群备份 ​

pg_dump 只能导出一个数据库,而一个 PostgreSQL 集群里除了业务库,还有一类「不属于任何单个数据库」的对象:角色(role)、表空间(tablespace)、以及配置参数的权限授予。这些对象被所有数据库共享,pg_dump 不会碰它们。集群级对象丢失的后果往往比丢一个业务库更麻烦——恢复脚本里满屏的 role "app_user" does not exist、tablespace "ssd" does not exist,最后不得不手工去比对原库的 pg_roles。

pg_dumpall 就是为解决这个问题而生的:它把整个集群导出成一个 SQL 脚本。实现方式是先导出全局对象,再对集群里的每一个数据库依次调用 pg_dump,把所有输出拼接成一份脚本。

理解这个实现方式非常关键,因为它直接决定了两条重要结论:第一,pg_dumpall 的输出只能是纯文本 SQL(多个数据库的导出无法塞进一个归档文件);第二,它是「逐库依次导出」的,因此不保证跨数据库的一致性——库 A 导出时刻的状态与库 B 可能相差几分钟。

前置知识

建议先阅读:pg_dump 基础用法、表空间

基础概念 ​

pg_dumpall 与 pg_dump 的分工 ​

维度pg_dumppg_dumpall
作用范围单个数据库整个集群
全局对象(角色 / 表空间 / 参数权限)不包含包含
输出格式plain / custom / directory / tar仅 plain(纯文本 SQL)
可并行支持(-Fd -j)不支持
可选择性恢复支持(pg_restore)只能整段执行
跨库一致性不涉及不保证
需要权限表读权限即可通常需要超级用户
密码认证一次连接一次认证每库一次连接,各认证一次

两者的关系不是替代而是互补:

  • pg_dumpall -g 负责「集群骨架」:角色、表空间、参数权限;
  • pg_dump -Fc 负责「每个库的血肉」:定义与数据。

这也是生产环境推荐的组合策略,后文会展开。

全局对象到底包含什么 ​

按官方定义,pg_dumpall 额外导出的全局对象有三类:

类别对应 SQL说明
数据库角色CREATE ROLE / ALTER ROLE含成员关系、属性、默认密码
表空间CREATE TABLESPACE含 I/O 参数、位置路径
配置参数权限ALTER ROLE ... SET、GRANT SET ON PARAMETER角色级参数默认值与设置权限
数据库本身CREATE DATABASE每个库一条,含 owner / encoding / locale

另外,pg_dumpall 的输出中会出现 \connect(psql 元命令),用于在脚本执行过程中切换到每个数据库。这是纯文本脚本特有的机制,归档格式没有等价物。

输出脚本的骨架 ​

一份完整的 pg_dumpall 输出大致是下面的结构:

text
--
-- PostgreSQL database cluster dump
--

-- 1) 全局对象:角色
CREATE ROLE app_user;
ALTER ROLE app_user WITH LOGIN NOSUPERUSER ...;
ALTER ROLE app_user WITH PASSWORD 'SCRAM-SHA-256$4096:...';

-- 2) 全局对象:表空间
CREATE TABLESPACE ssd OWNER postgres LOCATION '/data/tblspc/ssd';

-- 3) 数据库定义
CREATE DATABASE orders WITH TEMPLATE = template0 ENCODING = 'UTF8';
ALTER DATABASE orders OWNER TO app_user;

-- 4) 切库并导出内容
\connect orders
-- ...此处是该库的 pg_dump 输出(定义 + 数据 + 权限)...

\connect analytics
-- ...下一个库...

-- 5) 收尾:参数权限等
-- PostgreSQL database cluster dump complete

三个值得注意的细节:

  1. 角色密码默认会被导出,且是 SCRAM-SHA-256 校验子(salt + verifier),不是明文;
  2. \connect 依赖 psql,因此恢复必须用 psql -f,不能用 pg_restore;
  3. 开头必须先连上某个库才能导全局对象,默认连 postgres,不存在则退到 template1。

为什么不能保证跨库一致性 ​

pg_dumpall 内部对每个库调用一次 pg_dump,每次调用是独立的连接、独立的快照。假设集群里有 10 个库,每个库导出耗时 5 分钟:

text
时间轴 →
库 A  |==== 快照点 t0 ====|
库 B       |==== 快照点 t5 ====|
库 C             |==== 快照点 t10 ====|
...
库 J                    |==== 快照点 t45 ====|

如果业务在库 A 和库 B 之间有跨库逻辑关联(比如 A 记订单、B 记库存),那么导出的两份数据可能对不上——A 里有一笔订单,B 里的库存扣减可能发生在 B 的快照之后而没被记录。

官方对此没有「一致性」承诺,文档中强调的是「导出整个集群的脚本」,而不是「一致地导出」。如果需要多库同一时刻的一致性,必须用 pg_export_snapshot() + pg_dump --snapshot 手工绑定,或者干脆改用物理备份。

逐库连接与认证 ​

pg_dumpall 需要多次连接服务端(每个库一次,还要为全局对象连一次)。这带来两个实际影响:

  • 密码认证会反复提示。用 -W 时每个库都要输一次密码,几乎无法忍受;正确做法是配置 ~/.pgpass;
  • 连接次数多,成本不低。大集群(上百个库)执行 pg_dumpall 会产生上百次连接,注意 max_connections 与连接开销。

密码敏感性 ​

默认情况下,角色密码以 SCRAM-SHA-256 校验子的形式出现在脚本里:

sql
ALTER ROLE app_user WITH PASSWORD 'SCRAM-SHA-256$4096:...salt...:...storedkey:serverkey...';

它虽然不可逆,但足以用于离线暴力破解(尤其弱密码),因此这份脚本必须当作机密文件对待:权限设为 0600、不落公共目录、不进代码仓库。

如果目标是「只要结构不要密码」,用 --no-role-passwords:

bash
pg_dumpall -g --no-role-passwords -f globals_nopass.sql

该选项的行为有两个重要后果,官方文档写得很明确:

  1. 恢复后角色密码为 NULL,密码认证会一直失败,直到重新设置密码;
  2. 因为不需要密码,角色信息改为从视图 pg_roles 读取(而不是 pg_authid),因此在 pg_authid 访问受限的环境下也能用。

权限要求 ​

导出时:pg_dumpall 要读取所有库的表,因此通常必须以超级用户身份连接才能导出完整内容。非超级用户在遇到无权限的库时会报错跳过。

恢复时:脚本里包含 CREATE ROLE 与 CREATE DATABASE,因此执行脚本的账号也必须有超级用户权限,否则会在第一批语句就失败。

关键术语 ​

术语含义
集群(cluster)一个 PostgreSQL 实例管理的数据目录及其下所有数据库
全局对象(global objects)角色、表空间、参数权限等跨库共享对象
\connectpsql 元命令,切换到指定数据库继续执行脚本
参数权限GRANT SET ON PARAMETER 授予的设置配置参数的能力
SCRAM-SHA-256PostgreSQL 10+ 默认的密码认证方式,脚本中存的是校验子
逐库快照pg_dumpall 对每个库各自取快照,彼此时间点不同

语法与用法 ​

基本语法 ​

bash
pg_dumpall [connection-option...] [option...]

pg_dumpall 没有 dbname 位置参数:它的目标永远是整个集群。连接参数与 pg_dump 基本一致。

最简示例 ​

bash
# 全集群导出到文件
pg_dumpall -h 10.0.0.5 -U postgres -w -f /backup/cluster_$(date +%F).sql

# 只导全局对象(角色 + 表空间)
pg_dumpall -g -f /backup/globals.sql

# 只导角色
pg_dumpall -r -f /backup/roles.sql

# 只导表空间
pg_dumpall -t -f /backup/tablespaces.sql

内容范围参数 ​

参数长选项含义
-g--globals-only只导全局对象(角色 + 表空间 + 参数权限),不含任何库
-r--roles-only只导角色
-t--tablespaces-only只导表空间
-a--data-only只导数据
-s--schema-only只导结构
--no-data—不导数据
--no-schema—不导结构
--statistics-only—只导优化器统计信息

注意 -t 在 pg_dumpall 中的含义与 pg_dump 完全不同:在 pg_dump 里 -t 是 --table,在 pg_dumpall 里是 --tablespaces-only。这是跨工具使用时最容易搞错的参数。

连接与目标库参数 ​

参数长选项含义
-h--host主机
-p--port端口
-U--username用户名
-d--dbname连接字符串(库名会被忽略)
-l--database用于导出全局对象与发现其他库的初始库名
-W--password提示密码
-w--no-password不提示密码
--role—连接后执行 SET ROLE,以指定角色身份导出

-d 与 -l 极易混淆,务必分清:

  • -d 传的是连接参数串(如 host=10.0.0.5 port=5432 user=postgres),其中即使写了库名也会被忽略;
  • -l 传的是库名,决定全局对象从哪里读、从哪里发现其他库;不给时默认 postgres,postgres 不存在则用 template1。
bash
# -l 指定初始库为 template1(当 postgres 库被删掉时)
pg_dumpall -l template1 -f /backup/cluster.sql

过滤与排除 ​

bash
# 排除匹配的数据库(可重复)
pg_dumpall --exclude-database='tmp_*' -f /backup/cluster.sql

# 用过滤文件批量排除(PG17+)
pg_dumpall --filter=exclude.txt -f /backup/cluster.sql

过滤文件每行一条规则,格式固定:

text
# 注释行以 # 开头
exclude database tmp_*
exclude database scratch_*

--filter 与 --exclude-database 可以同时使用,--filter 也可以多次指定。文件名写 - 表示从标准输入读取。

输出与清理控制 ​

bash
# 先 DROP 后 CREATE,用于覆盖已有集群
pg_dumpall -c -f /backup/cluster.sql

# DROP 时加 IF EXISTS,抑制「does not exist」错误
pg_dumpall -c --if-exists -f /backup/cluster.sql

# 不导出 GRANT / REVOKE
pg_dumpall -x -f /backup/cluster_nopriv.sql

# 不导出属主变更
pg_dumpall -O -f /backup/cluster_noowner.sql

--if-exists 必须与 --clean 一起使用,单独给出无效。

控制导出内容细项 ​

bash
# 不导出表空间定义与归属
pg_dumpall --no-tablespaces -f /backup/cluster.sql

# 不导出 RLS 策略
pg_dumpall --no-policies -f /backup/cluster.sql

# 不导出发布 / 订阅
pg_dumpall --no-publications --no-subscriptions -f /backup/cluster.sql

# 不导出 unlogged 表的数据(结构仍导出)
pg_dumpall --no-unlogged-table-data -f /backup/cluster.sql

一致性与锁等待 ​

bash
# 等锁超时即失败,不无限等待
pg_dumpall --lock-wait-timeout=30s -f /backup/cluster.sql

# 跨大版本导出时强制给所有标识符加引号
pg_dumpall --quote-all-identifiers -f /backup/cluster.sql

详细说明 ​

全局对象导出:-g 的典型用法 ​

-g 是 pg_dumpall 使用频率最高的模式。它的产物是一份「集群骨架」脚本,可以在任何数据库之前或之后执行(只要有超级用户权限):

bash
pg_dumpall -h 10.0.0.5 -U postgres -w -g -f /backup/globals.sql
text
--
-- Roles
--
CREATE ROLE app_user;
ALTER ROLE app_user WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB LOGIN NOREPLICATION NOBYPASSRLS;
ALTER ROLE app_user WITH PASSWORD 'SCRAM-SHA-256$4096:...';
CREATE ROLE readonly;
ALTER ROLE readonly WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS;
GRANT readonly TO app_user;

--
-- Tablespaces
--
CREATE TABLESPACE ssd OWNER postgres LOCATION '/data/tblspc/ssd';

--
-- Databases
--
CREATE DATABASE orders WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8';
ALTER DATABASE orders OWNER TO app_user;

注意即使只加 -g,脚本里仍然包含 CREATE DATABASE(属于「发现的其他库」信息的一部分)。如果确实只要角色,用 -r;只要表空间,用 -t。

角色导出:-r ​

bash
pg_dumpall -r --no-role-passwords -f /backup/roles_nopass.sql

-r 只导角色,不含表空间与数据库。它适合「在新集群上先把账号体系建好,再导业务库」的流程,也适合定期对角色定义做版本化快照——角色被误删或误改权限时,这份文件就是救命稻草。

--no-role-passwords 与 -r 是天然搭配:只同步账号结构与成员关系,密码由各自的密钥管理系统注入。

表空间导出:-t ​

bash
pg_dumpall -t -f /backup/tablespaces.sql

产物形如:

sql
CREATE TABLESPACE fast OWNER postgres LOCATION '/data/fast';
CREATE TABLESPACE archive OWNER postgres LOCATION '/data/archive';

恢复前必须保证目标服务器上这些目录已存在且属主正确,否则 CREATE TABLESPACE 会失败。这是表空间类恢复事故的头号原因:脚本没问题,但 /data/fast 这个目录在新机器上不存在。

同时提醒:CREATE TABLESPACE 不能运行在事务块内,因此如果恢复脚本被包在 --single-transaction 之类的包装中会失败。

数据装载方式 ​

bash
# 用多值 INSERT,每 500 行一条(兼顾体积与速度)
pg_dumpall --rows-per-insert=500 -f /backup/cluster.sql

# 逐行 INSERT + 显式列名(便于导入到非 PostgreSQL 系统)
pg_dumpall --column-inserts -f /backup/cluster.sql

# 配合 ON CONFLICT DO NOTHING 实现幂等导入
pg_dumpall --column-inserts --on-conflict-do-nothing -f /backup/cluster.sql

默认是 COPY,最快也最小。只有当目标不是「原样恢复」而是「把数据搬进另一个体系」时,才值得付出 INSERT 的代价。

--on-conflict-do-nothing 必须搭配 --inserts 或 --column-inserts 使用。

分区表的特殊处理 ​

bash
# 让分区数据统一走根表装载,由目标端重新路由到正确分区
pg_dumpall --load-via-partition-root -f /backup/cluster.sql

这个选项解决一个很具体的问题:分区表的 COPY 默认直接写进各个叶子分区。如果两个系统的排序规则(collation)定义不同,同一行数据在目标端可能本该属于另一个分区。让数据统一从根表走,目标端会按自己的分区规则重新判定归属。

典型场景是分区键为 text 且依赖 collation 排序,而源端与目标端的 LC_COLLATE / collation 版本不一致。

密码与认证细节 ​

bash
# 每库都要认证,.pgpass 是唯一可接受的方式
chmod 600 ~/.pgpass
pg_dumpall -h 10.0.0.5 -U postgres -w -f /backup/cluster.sql

.pgpass 的匹配规则是按 hostname:port:database:username:password 逐字段匹配,pg_dumpall 连不同库时 database 字段各不相同,因此用 * 通配数据库名是必要写法:

text
10.0.0.5:5432:*:postgres:MyS3cret

如果只写了具体的库名,其他库的连接就会回退到交互式提示,脚本在无人值守场景下直接卡死。

恢复必须使用 psql ​

bash
# 正确:脚本是纯文本,必须交给 psql 执行
psql -h new-host -U postgres -d postgres -v ON_ERROR_STOP=1 \
  -f /backup/cluster.sql

# 错误:pg_dumpall 的输出不是归档,pg_restore 无法读取
pg_restore -d postgres /backup/cluster.sql   # 会失败

psql 的 -v ON_ERROR_STOP=1 很关键:默认情况下 psql 遇到错误会继续执行后续语句,一个早期的 CREATE ROLE 失败可能导致后面几百条语句在错误的前提下「成功」。加上它可以让脚本在第一个错误处停下。

另一个必须注意的点:恢复时必须连接 postgres(或 template1)这类必然存在的库,因为脚本开头的全局对象语句与 CREATE DATABASE 都与当前库无关,但它们需要一个已存在的连接。连一个不存在的库会导致 psql 直接启动失败。

bash
# 默认连 postgres 库(推荐)
psql -h new-host -U postgres -f /backup/cluster.sql

# 若 postgres 库不存在,连 template1
psql -h new-host -U postgres -d template1 -f /backup/cluster.sql

组合策略:全局对象 + 各库归档 ​

这是生产环境最推荐的形态,兼顾了「集群完整性」与「单库可选择性恢复」:

bash
#!/usr/bin/env bash
set -euo pipefail
DATE=$(date +%F)
DIR=/backup/pg/${DATE}
mkdir -p "$DIR"

# 1) 集群骨架:角色 + 表空间
pg_dumpall -h 10.0.0.5 -U postgres -w -g -f "$DIR/globals.sql"

# 2) 逐个业务库用 pg_dump 导成归档
for DB in orders analytics billing; do
  pg_dump -h 10.0.0.5 -U postgres -w -Fc -Z zstd:6 \
    -f "$DIR/${DB}.dump" "$DB"
done

恢复时先执行 globals.sql 建好账号与表空间,再用 pg_restore 逐库恢复:

bash
psql -h new-host -U postgres -v ON_ERROR_STOP=1 -f "$DIR/globals.sql"
for DB in orders analytics billing; do
  createdb -h new-host -U postgres "$DB"
  pg_restore -h new-host -U postgres -d "$DB" --no-owner -j 4 "$DIR/${DB}.dump"
done

这个方案的三个好处:

  1. 每个库是独立归档,可以单独恢复某一张表、某一段数据;
  2. 可以并行(多个库的 pg_dump -j 甚至可以同时跑);
  3. 全局对象单独成文件,恢复顺序明确可控。

代价是:仍然没有跨库一致性,且脚本要自己维护库清单。

大规模集群的替代方案 ​

pg_dumpall 的逐库串行特性在大集群上是致命的:200 个库 × 每库 3 分钟 ≈ 10 小时,且期间没有跨库一致性。业务规模上去之后,实践中会转向:

方案优势代价
pg_dumpall -g + 并行 pg_dump -Fd -j灵活、可并行、可选择性恢复需自维护库清单与调度
物理备份 pg_basebackup一次搞定整个集群、速度快不能跨版本、只能整集群恢复
pgBackRest / Barman增量、压缩、校验、保留策略、云存储需要额外组件与学习成本
逻辑复制 / 发布订阅可做在线迁移与部分同步不是备份,DDL 需另行同步

判断标准很简单:恢复时间目标(RTO)能否被「逐库串行恢复」满足。如果不行,就该往物理备份或专业备份工具迁移。

实战示例 ​

示例一:每日全集群备份 ​

bash
#!/usr/bin/env bash
set -euo pipefail
DATE=$(date +%F_%H%M)
OUT=/backup/pg/cluster_${DATE}.sql

# 需要超级用户;-w 配合 .pgpass 实现免交互
pg_dumpall \
  -h 10.0.0.5 -p 5432 -U postgres -w \
  --verbose \
  -f "$OUT" 2> "${OUT}.log"

# 压缩存储(脚本本身是文本,压缩比通常很高)
gzip -9 "$OUT"

# 校验脚本以预期结尾(一个廉价而有效的完整性检查)
zcat "${OUT}.gz" | tail -3
text
--
-- PostgreSQL database cluster dump complete
--

示例二:只导出角色与表空间,重建集群骨架 ​

bash
# 导出(不含密码,便于进版本库)
pg_dumpall -h 10.0.0.5 -U postgres -w -g \
  --no-role-passwords \
  -f /backup/globals_$(date +%F).sql

grep -c 'CREATE ROLE' /backup/globals_*.sql
grep -c 'CREATE DATABASE' /backup/globals_*.sql
text
14
6
bash
# 在新集群上恢复骨架
psql -h new-host -U postgres -v ON_ERROR_STOP=1 \
  -f /backup/globals_$(date +%F).sql

示例三:排除临时库后导出 ​

bash
cat > /tmp/exclude.txt <<'EOF'
# 排除临时库与测试库
exclude database tmp_*
exclude database scratch_*
exclude database ci_*
EOF

pg_dumpall -h 10.0.0.5 -U postgres -w \
  --filter=/tmp/exclude.txt \
  -f /backup/cluster_prod.sql

# 确认被排除的库没有出现在脚本里
grep -E '^CREATE DATABASE' /backup/cluster_prod.sql
text
CREATE DATABASE orders WITH TEMPLATE = template0 ENCODING = 'UTF8';
CREATE DATABASE analytics WITH TEMPLATE = template0 ENCODING = 'UTF8';
CREATE DATABASE billing WITH TEMPLATE = template0 ENCODING = 'UTF8';

示例四:跨大版本迁移的完整流程 ​

bash
# 步骤 1:源端(PG 14)导出骨架
/usr/pgsql-18/bin/pg_dumpall -h old-host -U postgres -w \
  -g --quote-all-identifiers \
  -f /tmp/globals14.sql

# 步骤 2:逐个业务库导出为归档(用 18 的客户端工具连 14 的服务端)
for DB in orders analytics; do
  /usr/pgsql-18/bin/pg_dump -h old-host -U postgres -w \
    -Fc -Z zstd:6 -f "/tmp/${DB}.dump" "$DB"
done

# 步骤 3:目标端(PG 18)先恢复骨架
psql -h new-host -U postgres -v ON_ERROR_STOP=1 -f /tmp/globals14.sql

# 步骤 4:恢复数据
for DB in orders analytics; do
  /usr/pgsql-18/bin/pg_restore -h new-host -U postgres \
    -d "$DB" --no-owner -j 4 "/tmp/${DB}.dump"
done

# 步骤 5:目标端跑 ANALYZE,让优化器统计信息可用
for DB in orders analytics; do
  /usr/pgsql-18/bin/psql -h new-host -U postgres -d "$DB" -c 'ANALYZE;'
done

示例五:校验导出结果 ​

bash
# 完整性与对象计数校验
grep -q 'PostgreSQL database cluster dump complete' /backup/cluster.sql && echo "script complete"

更实际的校验手段是计数比对:导出前后对关键对象计数,恢复后再比对。

sql
-- 导出前记录基线
SELECT count(*) AS db_count FROM pg_database WHERE datistemplate = false;
SELECT count(*) AS role_count FROM pg_roles WHERE rolname NOT LIKE 'pg\_%';
SELECT count(*) AS tablespace_count FROM pg_tablespace WHERE spcname NOT LIKE 'pg\_%';
text
 db_count
----------
        6
(1 row)

 role_count
------------
         14
(1 row)

 tablespace_count
------------------
                2
(1 row)
bash
# 从脚本里数一遍,二者应当一致
grep -c '^CREATE DATABASE' /backup/cluster.sql
grep -c '^CREATE ROLE' /backup/cluster.sql
grep -c '^CREATE TABLESPACE' /backup/cluster.sql

注意事项 ​

pg_dumpall 不保证跨库一致性

这是最重要的限制。每个库各自取快照,导出时刻不同:

text
库 A 快照 t0 ──► 库 B 快照 t5 ──► 库 C 快照 t12

跨库有业务不变量的场景,不要指望 pg_dumpall 的输出能直接对账。需要一致性就用 --snapshot 手工绑定,或改用物理备份。

输出只能是纯文本,不能用 pg_restore

bash
# 错误
pg_restore -d postgres /backup/cluster.sql
# 正确
psql -d postgres -f /backup/cluster.sql

恢复必须连接一个已存在的库

psql 连不上不存在的库就无法开始执行脚本:

bash
# 若目标集群里 postgres 库已被删除,这条会直接失败
psql -h new-host -U postgres -d postgres -f cluster.sql
# 改用 template1
psql -h new-host -U postgres -d template1 -f cluster.sql

-t 的含义与 pg_dump 不同

bash
pg_dump    -t public.orders   # --table,导某张表
pg_dumpall -t                 # --tablespaces-only,只导表空间

--no-role-passwords 会让密码认证全部失效

恢复后角色密码为 NULL,必须逐个重设,否则应用连不上:

bash
# 恢复后需要重建密码
psql -c "ALTER ROLE app_user WITH PASSWORD 'new-secret';"

密码校验子在脚本里等于泄露风险

即使不是明文,SCRAM 校验子也可能被离线爆破。脚本必须 0600、加密存储、不进 Git:

bash
umask 077
pg_dumpall -g -f /backup/globals.sql
ls -l /backup/globals.sql
text
-rw------- 1 postgres postgres 8123 Sep 30 03:00 /backup/globals.sql

CREATE TABLESPACE 的目标目录必须预先存在

脚本不会帮你建目录:

bash
# 恢复前先在目标机准备好
mkdir -p /data/tblspc/ssd
chown postgres:postgres /data/tblspc/ssd
chmod 700 /data/tblspc/ssd

CREATE TABLESPACE 与 CREATE DATABASE 不能在事务块内

因此不要把整个脚本包进 BEGIN ... COMMIT,也不要指望用单事务恢复获得原子性。

-W 在 pg_dumpall 里会反复提示

每个库一次认证:

bash
# 几乎不可用
pg_dumpall -W -f cluster.sql
# 正确做法
chmod 600 ~/.pgpass   # 内容形如 host:port:*:user:pass
pg_dumpall -w -f cluster.sql

非超级用户导出会静默丢数据

无权限的库会被报错跳过,脚本看起来「成功」了,实际缺了好几个库。务必用超级用户,或至少检查日志中的权限错误。

恢复脚本会以源端超级用户的身份执行任意 SQL

官方有明确安全警告。处理不可信来源的脚本前必须人工审阅。

--no-sync 只适合测试

它跳过「等待数据落盘」,机器崩溃会留下损坏的 dump:

bash
# 不要在生产备份脚本里用
pg_dumpall --no-sync -f cluster.sql

最佳实践 ​

  1. 生产环境用「pg_dumpall -g + 逐库 pg_dump -Fc」组合,而不是单个 pg_dumpall 全量。这样既保住集群骨架,又保留了单库选择性恢复与并行能力。
bash
pg_dumpall -g -f "$DIR/globals.sql"
for DB in $(psql -Atc "SELECT datname FROM pg_database WHERE datistemplate=false AND datname<>'postgres'"); do
  pg_dump -Fc -f "$DIR/$DB.dump" "$DB"
done
  1. 明确接受「无跨库一致性」这个前提,并为跨库业务设计对账或校验机制。不要把它当成原子快照来用。

  2. 备份账号用 .pgpass(* 通配数据库名),不要用 -W。pg_dumpall 会多次连接,交互式密码输入在自动化场景下必然失败。

text
10.0.0.5:5432:*:postgres:MyS3cret
  1. 只要结构不要密码时用 --no-role-passwords,并把它写进恢复流程——恢复后必须有一步重设密码,否则应用全部认证失败。

  2. 恢复脚本一律用 psql -v ON_ERROR_STOP=1,让第一个错误就中止,避免在错误前提下「继续成功」。

bash
psql -h new-host -U postgres -v ON_ERROR_STOP=1 -d postgres -f cluster.sql
  1. 恢复前先准备好表空间目录并验证属主,把「目录不存在」这类低级失败挡在恢复开始之前。

  2. 给全局对象脚本单独做版本化归档。角色与权限的变更频率低,但影响面极大;单独保存 -g 的输出,可以随时回答「上周这个角色的权限是什么」。

  3. 用 --quote-all-identifiers 处理跨大版本导出,避免目标版本保留字集合变化导致的语法错误。

  4. 导出后做对象计数校验,比对 pg_database / pg_roles / pg_tablespace 的数量与脚本中的 CREATE 语句数:

sql
SELECT count(*) FROM pg_database WHERE datistemplate = false;
SELECT count(*) FROM pg_roles WHERE rolname NOT LIKE 'pg\_%';
  1. 定期做恢复演练,重点验证两件事:所有库都能建起来,以及应用账号能按预期连上并有正确权限。备份文件存在不等于恢复可用。

  2. 大集群改用 pgBackRest / Barman 或物理备份。当库数量与恢复时间目标冲突时,pg_dumpall 的逐库串行模型就是瓶颈,应尽早迁移到支持增量与并行校验的工具。

  3. 脚本按机密文件管理:umask 077、独立目录、独立保留策略,不与业务代码同仓库,不投递到不受控的共享存储。

bash
umask 077
pg_dumpall -g -f /backup/globals.sql

下一节

继续学习:pg_restore 恢复

参考链接 ​