Skip to content

pg_dump 基础用法 ​

pg_dump 是 PostgreSQL 自带的逻辑备份工具,它把一个数据库的内容导出成一个可读的 SQL 脚本或归档文件。所谓「逻辑」,指的是它导出的不是磁盘上的数据文件,而是重建数据库所需的 SQL 语句与数据行——恢复时靠 PostgreSQL 重新执行这些语句,从头建库、建表、写数据。

它是日常使用频率最高的备份命令:逻辑备份可以跨大版本升级(把 14 的数据恢复到 18)、跨 CPU 架构迁移、跨云厂商搬库,甚至在人工修改后导入到其他数据库产品。这些能力是物理备份(直接拷数据文件)做不到的。

但 pg_dump 也有明确的边界:它一次只能导出一个数据库,不包含角色、表空间这类集群级全局对象;它的导出速度受 SQL 解析与网络传输限制,超大库往往不如物理备份快。理解这些能力与代价,才能决定什么时候用它、什么时候不该用它。

前置知识

建议先阅读:行级安全使用场景、WAL 与检查点

基础概念 ​

逻辑备份与物理备份的定位 ​

维度逻辑备份(pg_dump)物理备份(pg_basebackup 等)
备份对象数据库对象的定义与数据行数据目录下的物理文件
输出形式SQL 脚本 / 归档文件可直接启动的 PGDATA 目录
跨大版本支持(14 → 18)不支持,必须同主版本
跨架构支持(x86 → ARM)不支持
单表恢复支持不支持(整库为单位)
恢复粒度表 / 模式 / 数据 / 结构整个集群
增量能力无(每次全量)支持(WAL / 增量备份)
时间点恢复不支持支持(配合 WAL 归档)
速度慢(要靠 SQL 重建)快(顺序拷贝文件)
体积通常更小(可压缩、去碎片)通常更大(含空洞与索引页)

一句话概括:逻辑备份换的是灵活性与可移植性,物理备份换的是速度与时间点恢复能力。生产环境常见做法是两者并存:物理备份负责「整库快速回滚」,逻辑备份负责「单表误删找回」和「跨版本迁移」。

一致性是如何保证的 ​

pg_dump 最容易被误解的一点是「备份期间数据库还能不能写」。答案是能写。官方文档明确说明:pg_dump 会做一致性导出,即使数据库正在被并发使用;它不会阻塞其他用户的读写。

实现机制是快照:pg_dump 在开始时开启一个事务并取一个快照,此后所有表都以同一个快照读取。无论导出持续多久,读到的都是「备份开始时那一刻」的数据状态。

sql
-- pg_dump 内部等价于执行了这样一个事务
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- ...此处快照被固定,之后所有 SELECT 都看到同一时刻的数据...
-- 导出各个表的定义与数据
COMMIT;

在默认的 READ COMMITTED 隔离级别下,同一个事务里前后两条 SELECT 可能看到不同的数据;而 pg_dump 使用 REPEATABLE READ(在需要时提升到 SERIALIZABLE)级别,使得整个导出过程中每张表看到的都是同一时刻的数据。这就是「一致」的含义:不会出现 A 表已包含新订单、B 表还没包含该订单的中间状态。

锁模型:ACCESS SHARE 与 ACCESS EXCLUSIVE ​

pg_dump 对每张被导出的表加 ACCESS SHARE 锁。这种锁与 SELECT 使用的锁同级,与写操作(INSERT / UPDATE / DELETE)不冲突,但与 ACCESS EXCLUSIVE 冲突。

text
                   ACCESS SHARE   ACCESS EXCLUSIVE
ACCESS SHARE       兼容           冲突
ACCESS EXCLUSIVE   冲突           冲突

这意味着:

  • 普通 DML(增删改查)不会阻塞 pg_dump,pg_dump 也不会阻塞它们;
  • 任何需要 ACCESS EXCLUSIVE 的操作会与 pg_dump 冲突——DROP TABLE、TRUNCATE、ALTER TABLE、REINDEX、VACUUM FULL、CLUSTER 等;
  • 如果 pg_dump 已经持有 ACCESS SHARE,后续的 ALTER TABLE 会排队等待;反过来,如果 ALTER TABLE 先拿到锁,pg_dump 也必须等它释放。

这里有一个生产事故高发点:一个长时间的 ALTER TABLE 或 VACUUM FULL 可能让 pg_dump 长时间等待,进而整个备份窗口被拉长。反之,一个长时间运行的 pg_dump(备份大库)会让 DDL 排队,再后面所有查询也跟着排队。

与并发 DDL 的交互 ​

pg_dump 在导出过程中会重复读取系统目录(catalog)。如果在导出过程中有人执行了 DDL,可能产生以下后果:

  • 新增的表:不会出现在备份里,因为快照已经固定;
  • 被删除的表:pg_dump 可能已经记录了定义,但导出数据时发现表已不存在,此时会报错或跳过;
  • 被修改的表结构:可能出现「DDL 导出的是新结构、数据导出按旧结构」的矛盾,导致恢复脚本报错。

因此官方建议:备份窗口内尽量避免 DDL。归档格式(-Fc / -Fd)比纯文本格式容错性更好,因为它是延迟到恢复阶段才决定最终 SQL。

单库边界与全局对象 ​

pg_dump 只处理一个数据库。它不会导出:

  • 角色(CREATE ROLE)及其密码;
  • 表空间(CREATE TABLESPACE)定义;
  • 数据库本身的 CREATE DATABASE(除非加 -C);
  • 数据库级配置(ALTER DATABASE ... SET,除非加 -C);
  • 复制槽、订阅等其他集群级对象。

这些「全局对象」需要用 pg_dumpall -g 或单独的脚本维护。这是 pg_dump 与 pg_dumpall 最核心的分工,也是新手最容易遗漏的部分——用 pg_dump 恢复到新集群时,原数据库里的角色往往不存在,导致 ALTER OWNER 语句全部失败。

跨版本能力 ​

逻辑备份的输出是 SQL,因此低版本导出的数据可以恢复到高版本,这是大版本升级的标准手段之一。但要遵守一条官方规则:

pg_dump 的版本应当大于等于它所连接的服务端版本。低版本的 pg_dump 无法保证理解高版本服务端的新对象与新语法。

实践中的兼容矩阵:

pg_dump 版本可连接的 PostgreSQL 服务端版本
1818、17、16、15、14、13……
1717、16、15、14、13、12……
1616、15、14、13、12、11……
1414、13、12、11、10、9.6……

即:用新版本的 pg_dump 去连旧版本的服务端是安全的;用旧版本去连新版本则不受支持。跨大版本升级(如 14 → 18)时,正确做法是用 18 的 pg_dump 连 14 的库导出,再导入到 18。

关键术语 ​

术语含义
逻辑备份以 SQL 语句 / 数据行形式导出的备份,与存储格式无关
快照(snapshot)某个事务时刻可见的数据版本,pg_dump 全程使用同一个
归档格式(archive format)custom / directory / tar 三种非纯文本输出,需 pg_restore 恢复
TOCTable of Contents,归档内的对象目录清单
ACCESS SHAREpg_dump 对表加的锁,与 DML 兼容、与 DDL 冲突
全局对象角色、表空间、数据库定义等跨库共享的对象
大对象(large object)pg_largeobject 中存储的二进制数据,需显式控制是否导出
序列值(sequence value)序列当前值,通过 setval() 语句在恢复时还原

语法与用法 ​

基本语法 ​

bash
pg_dump [connection-option...] [option...] [dbname]

dbname 可以直接作为位置参数给出,也可以用 -d/--dbname 指定。连接参数与 psql 完全一致。

最简示例 ​

bash
# 导出为纯文本 SQL(默认格式),重定向到文件
pg_dump mydb > mydb.sql

# 显式用 -f 指定输出文件
pg_dump -f mydb.sql mydb

# 导出为自定义格式(压缩、可选择性恢复)
pg_dump -Fc -f mydb.dump mydb

# 导出为目录格式(支持并行)
pg_dump -Fd -j 4 -f mydb_dir mydb

连接参数 ​

参数长选项含义
-h--host服务端主机名或 socket 目录
-p--port端口,默认 5432
-U--username连接用户名
-d--dbname连接字符串或要导出的库名
-W--password强制提示输入密码
-w--no-password永不提示密码,缺密码时直接失败

-W 与 -w 的差别在自动化脚本里很重要:-w(小写)适合无人值守任务,缺密码立即失败而不是卡住等待输入。

环境变量 ​

不想每次敲连接参数时,用环境变量:

环境变量对应参数
PGHOST-h
PGPORT-p
PGUSER-U
PGPASSWORD密码(不推荐,见注意事项)
PGDATABASE默认库名
PGCLIENTENCODING客户端编码,等价于 -E

密码文件 .pgpass ​

.pgpass 是免交互提供密码的推荐方式。文件位于 ~/.pgpass(Windows 为 %APPDATA%\postgresql\pgpass.conf),每行格式:

text
hostname:port:database:username:password
bash
# 内容示例(通配符 * 表示匹配任意值)
# 10.0.0.5:5432:orders:backup:MyS3cret
# 注意权限必须是 0600,否则 PostgreSQL 会忽略该文件
chmod 600 ~/.pgpass

权限设置是关键:如果 .pgpass 权限过于开放,PostgreSQL 会直接忽略它,你会看到密码提示或认证失败。

详细说明 ​

输出格式:-F ​

-F 决定输出的形态,四种取值各有明确用途:

取值名称特点恢复方式
pplain纯文本 SQL 脚本,默认psql -f
ccustom单文件归档,默认压缩,可选择性恢复pg_restore
ddirectory每表一个文件的目录,默认 gzip,唯一支持并行pg_restore
ttar单文件 tar 归档,不支持压缩pg_restore
bash
# plain:最简单,但无法选择性恢复、无法并行
pg_dump -Fp -f mydb.sql mydb

# custom:最常用的归档格式,支持 pg_restore -l 查看清单
pg_dump -Fc -f mydb.dump mydb

# directory:支持 -j 并行导出,也方便用 Unix 工具逐文件处理
pg_dump -Fd -j 8 -f mydb_dir mydb

# tar:与 directory 内容等价,展开后就是目录格式
pg_dump -Ft -f mydb.tar mydb

压缩:-Z / --compress ​

-Z 控制归档格式的压缩。PostgreSQL 16 起,-Z 支持的不再只是压缩级别,而是「算法[:级别]」:

bash
# 传统写法:gzip,级别 6(默认级别因算法而异)
pg_dump -Fc -Z 6 -f mydb.dump mydb

# PG16+:显式指定算法
pg_dump -Fc -Z gzip -f mydb.dump mydb
pg_dump -Fc -Z lz4 -f mydb.dump mydb          # 压缩比低但速度极快
pg_dump -Fc -Z zstd -f mydb.dump mydb         # 压缩比高,速度也不错
pg_dump -Fc -Z zstd:9 -f mydb.dump mydb       # 指定级别
pg_dump -Fc -Z none -f mydb.dump mydb         # 不压缩

需要注意:tar 格式不支持压缩,给 -Ft 加 -Z 没有意义;plain 格式的 -Z 行为与归档格式不同,早期版本只能通过外部管道(如 | gzip)压缩。

并行导出:-j ​

bash
# 8 个 worker 并行导出,需要目录格式
pg_dump -Fd -j 8 -f /backup/mydb mydb

三个关键约束:

  1. 只能与 -Fd 目录格式搭配——这是唯一支持多进程同时写入的输出格式;
  2. pg_dump 会打开 njobs + 1 个数据库连接,必须确认 max_connections 足够;
  3. 服务端必须支持同步快照(synchronized snapshots)——该特性在 PostgreSQL 9.2 起支持主库、10 起支持备库。没有它,多个连接看到的快照可能不一致,备份将失去一致性保证。

并行导出的另一个副作用是死锁风险与主动中止:leader 进程会对后续 worker 要导出的对象预先加 ACCESS SHARE 锁,以防对象中途被删除。如果此时有客户端请求排他锁,worker 会以 NOWAIT 方式尝试加锁;一旦失败说明有人正在申请排他锁,pg_dump 只能直接中止备份而不是陷入死锁。

内容选择:-a / -s / -b / -B ​

bash
# 只导数据(表数据 + 大对象 + 序列值),不含定义与统计信息
pg_dump -a -f data.sql mydb

# 只导结构(对象定义),不含数据与统计信息
pg_dump -s -f schema.sql mydb

# 显式包含大对象(在配合 -n / -t / -s 时才有意义)
pg_dump -n app -b -f app.sql mydb

# 排除大对象
pg_dump -B -f smaller.sql mydb

几点容易踩坑的地方:

  • -a 与 -s 不能同时使用;
  • -a 与 --section=data 相似但历史原因并不完全等价;同理 -s 与 --section=pre-data --section=post-data 也不完全等价;
  • 大对象默认包含在普通导出中,但当指定了 -n / -t / -s / --no-data 时默认不再导出,需要 -b 显式加回;
  • --blobs 与 --no-blobs 是 -b / -B 的旧名,已废弃但仍可接受。

对象过滤:-t / -n / -N / -T ​

bash
# 只导某张表(可重复 -t 选择多张)
pg_dump -t 'public.orders' -f orders.sql mydb

# 通配符选择(注意加引号防止 shell 展开)
pg_dump -t 'public.orders_*' -f orders_all.sql mydb

# 只导某个 schema 及其全部对象
pg_dump -n app -f app.sql mydb

# 排除某个 schema
pg_dump -N audit -f no_audit.sql mydb

# 排除某些表
pg_dump -T 'public.logs_*' -f main.sql mydb

过滤规则要点:

  • -n 与 -N 同时给出时,结果是「匹配至少一个 -n 且不匹配任何 -N」的 schema;
  • -t 与 -T 同时给出时同理;
  • -t 会覆盖 -n / -N:用 -t 选中的表无论如何都会导出,而非表对象则完全不导出;
  • -t 除了表,还可以用来导出视图、物化视图、外部表、序列的定义(视图与物化视图的数据不会被导出);
  • 过滤是「不完整」的:-n / -t 不会自动带上被依赖的对象,因此单独导出的结果不保证能独立恢复到干净数据库。

DROP 与 CREATE:-c / -C / --if-exists ​

bash
# -c:先输出 DROP 语句,再输出 CREATE,用于覆盖已有对象
pg_dump -c -f mydb.sql mydb

# --if-exists:DROP 时加 IF EXISTS,避免对象不存在时报错
pg_dump -c --if-exists -f mydb.sql mydb

# -C:输出开头加上 CREATE DATABASE 并重连
pg_dump -C -f mydb.sql mydb

一个重要细节:-c 与 -C 在归档(非纯文本)格式下会被忽略。归档格式的 DROP / CREATE 是在 pg_restore 阶段通过 --clean / --create 指定的,因为归档只是一种「数据库对象清单 + 数据」的容器,最终生成什么 SQL 由恢复时决定。

-C 的额外行为:输出会包含该数据库的注释,以及所有本数据库特有的配置(ALTER DATABASE ... SET ...、ALTER ROLE ... IN DATABASE ... SET ...)和数据库级权限。

属主、权限与注释 ​

bash
# 不输出 ALTER OWNER / SET SESSION AUTHORIZATION,恢复后归恢复者所有
pg_dump -O -f portable.sql mydb

# 不导出 GRANT / REVOKE 等权限语句
pg_dump --no-privileges -f nopriv.sql mydb

# 不导出 COMMENT
pg_dump --no-comments -f nocomment.sql mydb

这三者共用是「跨环境搬运」的标准组合:目标环境往往没有同名角色、也没有相同的权限模型,带着 ALTER OWNER 和 GRANT 的脚本基本会大面积报错。

-O 在归档格式下同样会被忽略,需要在 pg_restore 时用 --no-owner。

数据装载方式:--inserts 系列 ​

默认情况下,plain 格式用 COPY 装载数据,速度最快。但 COPY 有局限性:在某些目标系统(如不兼容 COPY 的工具)中无法使用,或者你需要在数据行上做条件处理。此时可改用 INSERT:

bash
# 每行一条 INSERT
pg_dump --inserts -f mydb.sql mydb

# 每条 INSERT 显式列出列名(对列顺序变化的容忍度更高)
pg_dump --column-inserts -f mydb.sql mydb

# 每 100 行合并为一条多值 INSERT(折中方案,比逐行快很多)
pg_dump --rows-per-insert=100 -f mydb.sql mydb

# 生成 INSERT ... ON CONFLICT DO NOTHING,便于幂等导入
pg_dump --inserts --on-conflict-do-nothing -f mydb.sql mydb

代价很明确:INSERT 比 COPY 慢得多,且纯文本体积明显变大。--rows-per-insert 是速度与兼容性的折中点。

一致性与等待控制 ​

bash
# 使用指定的导出快照(需预先用 pg_export_snapshot() 取得)
pg_dump --snapshot='00000003-0000001B-1' -f mydb.sql mydb

# 等锁超时则失败,而不是无限等待
pg_dump --lock-wait-timeout=10s -f mydb.sql mydb

# 延迟到可串行化事务真正开始(避免对并发事务造成异常影响)
pg_dump --serializable-deferrable -f mydb.sql mydb

--serializable-deferrable 的用途较专门:普通 pg_dump 使用 REPEATABLE READ 快照。如果备份需要参与可串行化语义(例如备份的数据要用于与原库进行串行化一致性校验),加上这个选项会等一个「安全」的快照点再开始,代价是启动可能被推迟。

--snapshot 用于把多个 pg_dump 绑定到同一个快照,实现多库同时一致导出——这是绕开「pg_dump 只导单库」限制的一种手法。

编码与标识符 ​

bash
# 指定导出字符集
pg_dump -E UTF8 -f mydb.sql mydb

# 给所有标识符加双引号,避免关键字冲突
pg_dump --quote-all-identifiers -f mydb.sql mydb

--quote-all-identifiers 在跨大版本恢复时特别有用:如果目标版本的保留字集合比源版本更大,未加引号的标识符可能变成语法错误。全量加引号可以提前避免这类问题,代价是脚本更难读。

过滤文件:--filter(PG17+) ​

PostgreSQL 17 为 pg_dump 引入了 --filter,把过滤规则写进外部文件:

bash
pg_dump --filter=filter.txt -f mydb.dump -Fc mydb

该文件使用与 pg_restore --filter 相同的语法族:

text
# include / exclude + 对象类型 + 模式
include table public.orders_*
exclude table public.logs_*
include schema app

相比于一长串 -t / -T 参数,过滤文件更易维护、可纳入版本控制。这是较新的特性,使用前请确认服务端与客户端工具版本都 ≥ 17。

权限要求与监控 ​

导出所需的权限分两种情形:

  • 超级用户:可以导出任意数据库;
  • 普通用户:需要对被导出的表有 SELECT 权限。如果只想给「能读全库」的能力而不授予超级用户,可以使用预定义角色 pg_read_all_data:
sql
-- 授予某账号读取所有数据的能力(PostgreSQL 14+)
GRANT pg_read_all_data TO backup_user;

另外,pg_dump 需要读取系统目录,普通用户也需能读这些视图(默认可读)。如果备份的是 RLS 保护的表,注意 pg_dump 会以连接用户身份读数据,RLS 策略可能过滤掉不该丢的行——备份账号通常需要 BYPASSRLS 或属主身份。

监控进度用 --verbose:

bash
pg_dump --verbose -Fc -f mydb.dump mydb 2> dump.log

--verbose 会把详细的进度与耗时写到标准错误,便于判断卡在哪张表。

实战示例 ​

示例一:单库日常备份(自定义格式) ​

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

DATE=$(date +%F_%H%M)
BACKUP_DIR=/backup/pg
DB=orders

mkdir -p "$BACKUP_DIR"

pg_dump \
  -h 10.0.0.5 -p 5432 -U backup_user -w \
  -Fc \
  -Z zstd:9 \
  --verbose \
  -f "$BACKUP_DIR/${DB}_${DATE}.dump" \
  "$DB" 2> "$BACKUP_DIR/${DB}_${DATE}.log"

# 校验归档是否可读(列出 TOC 而不真正恢复)
pg_restore -l "$BACKUP_DIR/${DB}_${DATE}.dump" > /dev/null
echo "backup ok: ${DB}_${DATE}.dump"

示例二:并行导出大库 ​

bash
# 目录格式 + 8 并行;实测前请确认 max_connections 有冗余
time pg_dump -h 10.0.0.5 -U backup_user -w \
  -Fd -j 8 \
  -f /backup/pg/orders_dir \
  orders

# 查看目录内容(每个表一个 .dat.gz,加一个 toc.dat)
ls -lh /backup/pg/orders_dir | head
text
total 1.2G
-rw-r--r-- 1 postgres postgres  32K toc.dat
-rw-r--r-- 1 postgres postgres 480M 3001.dat.gz
-rw-r--r-- 1 postgres postgres 210M 3002.dat.gz
-rw-r--r-- 1 postgres postgres  95M 3003.dat.gz
-rw-r--r-- 1 postgres postgres  41M blob_16401.dat.gz

示例三:跨版本导出并导入 ​

bash
# 源库是 PostgreSQL 14,用 18 的 pg_dump 导出
/usr/pgsql-18/bin/pg_dump -h old-host -U app -Fc \
  -f /tmp/app_from14.dump appdb

# 目标 18 集群上恢复
createdb -h new-host -U postgres appdb
/usr/pgsql-18/bin/pg_restore -h new-host -U postgres \
  -d appdb --no-owner --no-privileges -j 4 /tmp/app_from14.dump

示例四:只导某几张表并单独恢复 ​

bash
# 导出两张表(含定义与数据)
pg_dump -t 'public.orders' -t 'public.order_items' \
  -Fc -f /tmp/two_tables.dump orders

# 先看清单,确认对象
pg_restore -l /tmp/two_tables.dump
text
;
; Archive created at 2026-09-30 10:12:33 CST
;     dbname: orders
;     TOC Entries: 12
;     Compression: zstd
;     Dump Version: 1.15-0
;     Format: CUSTOM
;
; Selected TOC Entries:
;
216; 1259 16401 TABLE public orders postgres
217; 1259 16402 TABLE public order_items postgres
3370; 0 16401 TABLE DATA public orders postgres
3371; 0 16402 TABLE DATA public order_items postgres
bash
# 恢复到目标库(先建好目标库)
pg_restore -d target_db --no-owner -t orders -t order_items /tmp/two_tables.dump

示例五:把表数据导出为可读 SQL ​

bash
# 逐行 INSERT + 不导 owner/权限,便于人工审阅与修改
pg_dump --inserts --column-inserts -O --no-privileges \
  -t 'public.orders' orders > orders_readable.sql

head -30 orders_readable.sql
text
--
-- PostgreSQL database dump
--

SET statement_timeout = 0;
SET lock_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;

CREATE TABLE public.orders (
    id bigint NOT NULL,
    tenant_id integer NOT NULL,
    amount numeric(12,2) NOT NULL,
    created_at timestamp with time zone DEFAULT now() NOT NULL
);

INSERT INTO public.orders (id, tenant_id, amount, created_at) VALUES (1, 42, 199.00, '2026-09-01 08:00:00+08');
INSERT INTO public.orders (id, tenant_id, amount, created_at) VALUES (2, 42, 88.50, '2026-09-01 09:15:00+08');

示例六:多库绑定同一快照导出 ​

bash
# 在第一个会话里导出快照 ID,保持事务打开
psql -h 10.0.0.5 -U backup_user -d db_a
sql
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT pg_export_snapshot();
text
 pg_export_snapshot
--------------------
 00000003-0000002A-1
(1 row)
bash
# 保持上面的事务不提交,在其他终端用同一快照导出两个库
pg_dump --snapshot='00000003-0000002A-1' -Fc -f db_a.dump db_a
pg_dump --snapshot='00000003-0000002A-1' -Fc -f db_b.dump db_b

注意事项 ​

备份期间严禁执行 DDL

ALTER TABLE、DROP TABLE、TRUNCATE 等需要 ACCESS EXCLUSIVE 锁,会与 pg_dump 的 ACCESS SHARE 冲突。轻则排队等待,重则导致并行备份中止:

sql
-- 备份期间执行这条可能让 pg_dump -j 直接失败
ALTER TABLE orders ADD COLUMN channel text;

-c / -C / -O 在归档格式下会被忽略

bash
# 错误预期:以为 -Fc 里已经带了 DROP / CREATE
pg_dump -Fc -c -C -f mydb.dump mydb    # -c -C 被静默忽略
# 正确做法:在恢复阶段指定
pg_restore --clean --create -d postgres mydb.dump

--clean 不加 --if-exists 会刷屏报错

纯文本恢复时,DROP 不存在的对象会产生大量「does not exist」错误信息,看起来像失败,实际无碍:

bash
pg_dump -c --if-exists -f mydb.sql mydb   # 推荐组合

-t 过滤出来的备份通常不能独立恢复

依赖的对象(序列、约束、索引、类型、函数)不会自动带上:

bash
# 危险:单表导出可能缺少序列默认值,恢复后主键无法自增
pg_dump -t public.orders -f only_orders.sql mydb

密码不要写在命令行或环境里

-W 提示输入或 .pgpass 才是安全做法。命令行参数对同机所有用户可见,PGPASSWORD 亦然:

bash
# 不安全(ps 可见)
pg_dump -W "mypass" ...

# 安全(权限 0600 的 .pgpass)
chmod 600 ~/.pgpass
pg_dump -w ...

大对象容易被漏掉

只要用了 -n / -t / -s,大对象默认不再导出,需要 -b 显式加回。很多「图片全都丢了」的事故源于此。

归档格式无法用文本编辑器直接修

-Fc / -Fd 是二进制 / 结构化归档,必须用 pg_restore 操作;想看内容用 pg_restore -l,想改清单用 -L。

恢复脚本会以源库超级用户的身份执行任意代码

官方有明确警告:恢复 dump 意味着目标端会执行源端超级用户所写的任意 SQL。不可信的源库,务必先审阅脚本(pg_restore --file 可把归档转成文本审阅)。

pg_dump -j 的锁排队会引发连锁阻塞

leader 持锁期间若有 DDL 排队,后续所有对该表的查询也会排在那条 DDL 之后,放大影响面。

最佳实践 ​

  1. 生产库优先用 -Fc 或 -Fd 归档格式,而不是默认的纯文本。归档格式支持选择性恢复、并行恢复、清单编辑,且默认压缩,是唯一能「只找回一张表」的备份形态。
bash
pg_dump -Fc -Z zstd:9 -f /backup/${DB}_$(date +%F).dump "$DB"
  1. 压缩算法按场景选。要备份速度选 lz4,要空间效率选 zstd,纯归档冷存储可用 zstd:9 以上;gzip 是兼容性最好但效率最低的默认项。

  2. 大库用目录格式 + -j 并行,并预留连接数。-j 8 需要额外的 8 条连接,务必确认 max_connections 有余量,否则会挤掉业务连接。

  3. 跨环境搬运一律加 --no-owner --no-privileges(归档格式在恢复时加),避免目标环境缺角色导致的恢复失败:

bash
pg_restore -d target --no-owner --no-privileges -j 4 /backup/app.dump
  1. 备份脚本必须包含「可读性校验」环节。备份文件写成功不等于可用:
bash
pg_restore -l "$DUMP" > /dev/null || { echo "archive corrupt: $DUMP"; exit 1; }
  1. 定期做真实恢复演练。备份的唯一价值是能恢复,建议至少每月把最新备份恢复到临时实例并跑一次行数 / 校验和比对。

  2. 备份与 DDL 变更窗口错开。发布日、数据库迁移日不要让备份任务与 DDL 抢锁;必要时用 --lock-wait-timeout 让备份主动失败而不是拖长窗口。

bash
pg_dump --lock-wait-timeout=30s -Fc -f "$DUMP" "$DB" || echo "backup skipped: lock timeout"
  1. 凭据用 .pgpass(0600)而不是 PGPASSWORD,并给备份账号最小权限——推荐 pg_read_all_data 而非超级用户:
sql
GRANT pg_read_all_data TO backup_user;
  1. 明确备份账号与 RLS 的关系。备份 RLS 表时,若账号不带 BYPASSRLS 又不是表属主,导出的数据会被策略过滤,产生「静默丢数据」的备份。用专用账号并显式确认。

  2. 用 --verbose 留日志,并记录耗时趋势。备份变慢往往是数据量增长或锁等待的信号:

bash
pg_dump --verbose -Fc -f "$DUMP" "$DB" 2>> /var/log/pg-backup.log
  1. 序列值必须显式确认已包含。默认会导出序列的 setval(),但配合 -s(仅结构)时不会。恢复后先验证序列当前值,避免主键冲突:
sql
SELECT last_value, is_called FROM orders_id_seq;
  1. 给备份文件做保留策略与异地副本。本地保留近 7 天 / 4 周,异地或对象存储保存月度归档,避免单点故障与误删。

下一节

继续学习:pg_dumpall 全集群备份

参考链接 ​