pg_dump 基础用法
pg_dump 是 PostgreSQL 自带的逻辑备份工具,它把一个数据库的内容导出成一个可读的 SQL 脚本或归档文件。所谓「逻辑」,指的是它导出的不是磁盘上的数据文件,而是重建数据库所需的 SQL 语句与数据行——恢复时靠 PostgreSQL 重新执行这些语句,从头建库、建表、写数据。
它是日常使用频率最高的备份命令:逻辑备份可以跨大版本升级(把 14 的数据恢复到 18)、跨 CPU 架构迁移、跨云厂商搬库,甚至在人工修改后导入到其他数据库产品。这些能力是物理备份(直接拷数据文件)做不到的。
但 pg_dump 也有明确的边界:它一次只能导出一个数据库,不包含角色、表空间这类集群级全局对象;它的导出速度受 SQL 解析与网络传输限制,超大库往往不如物理备份快。理解这些能力与代价,才能决定什么时候用它、什么时候不该用它。
基础概念
逻辑备份与物理备份的定位
| 维度 | 逻辑备份(pg_dump) | 物理备份(pg_basebackup 等) |
|---|---|---|
| 备份对象 | 数据库对象的定义与数据行 | 数据目录下的物理文件 |
| 输出形式 | SQL 脚本 / 归档文件 | 可直接启动的 PGDATA 目录 |
| 跨大版本 | 支持(14 → 18) | 不支持,必须同主版本 |
| 跨架构 | 支持(x86 → ARM) | 不支持 |
| 单表恢复 | 支持 | 不支持(整库为单位) |
| 恢复粒度 | 表 / 模式 / 数据 / 结构 | 整个集群 |
| 增量能力 | 无(每次全量) | 支持(WAL / 增量备份) |
| 时间点恢复 | 不支持 | 支持(配合 WAL 归档) |
| 速度 | 慢(要靠 SQL 重建) | 快(顺序拷贝文件) |
| 体积 | 通常更小(可压缩、去碎片) | 通常更大(含空洞与索引页) |
一句话概括:逻辑备份换的是灵活性与可移植性,物理备份换的是速度与时间点恢复能力。生产环境常见做法是两者并存:物理备份负责「整库快速回滚」,逻辑备份负责「单表误删找回」和「跨版本迁移」。
一致性是如何保证的
pg_dump 最容易被误解的一点是「备份期间数据库还能不能写」。答案是能写。官方文档明确说明:pg_dump 会做一致性导出,即使数据库正在被并发使用;它不会阻塞其他用户的读写。
实现机制是快照:pg_dump 在开始时开启一个事务并取一个快照,此后所有表都以同一个快照读取。无论导出持续多久,读到的都是「备份开始时那一刻」的数据状态。
-- 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 冲突。
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 服务端版本 |
|---|---|
| 18 | 18、17、16、15、14、13…… |
| 17 | 17、16、15、14、13、12…… |
| 16 | 16、15、14、13、12、11…… |
| 14 | 14、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 恢复 |
| TOC | Table of Contents,归档内的对象目录清单 |
ACCESS SHARE | pg_dump 对表加的锁,与 DML 兼容、与 DDL 冲突 |
| 全局对象 | 角色、表空间、数据库定义等跨库共享的对象 |
| 大对象(large object) | pg_largeobject 中存储的二进制数据,需显式控制是否导出 |
| 序列值(sequence value) | 序列当前值,通过 setval() 语句在恢复时还原 |
语法与用法
基本语法
pg_dump [connection-option...] [option...] [dbname]dbname 可以直接作为位置参数给出,也可以用 -d/--dbname 指定。连接参数与 psql 完全一致。
最简示例
# 导出为纯文本 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),每行格式:
hostname:port:database:username:password# 内容示例(通配符 * 表示匹配任意值)
# 10.0.0.5:5432:orders:backup:MyS3cret
# 注意权限必须是 0600,否则 PostgreSQL 会忽略该文件
chmod 600 ~/.pgpass权限设置是关键:如果 .pgpass 权限过于开放,PostgreSQL 会直接忽略它,你会看到密码提示或认证失败。
详细说明
输出格式:-F
-F 决定输出的形态,四种取值各有明确用途:
| 取值 | 名称 | 特点 | 恢复方式 |
|---|---|---|---|
p | plain | 纯文本 SQL 脚本,默认 | psql -f |
c | custom | 单文件归档,默认压缩,可选择性恢复 | pg_restore |
d | directory | 每表一个文件的目录,默认 gzip,唯一支持并行 | pg_restore |
t | tar | 单文件 tar 归档,不支持压缩 | pg_restore |
# 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 支持的不再只是压缩级别,而是「算法[:级别]」:
# 传统写法: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
# 8 个 worker 并行导出,需要目录格式
pg_dump -Fd -j 8 -f /backup/mydb mydb三个关键约束:
- 只能与
-Fd目录格式搭配——这是唯一支持多进程同时写入的输出格式; pg_dump会打开njobs + 1个数据库连接,必须确认max_connections足够;- 服务端必须支持同步快照(synchronized snapshots)——该特性在 PostgreSQL 9.2 起支持主库、10 起支持备库。没有它,多个连接看到的快照可能不一致,备份将失去一致性保证。
并行导出的另一个副作用是死锁风险与主动中止:leader 进程会对后续 worker 要导出的对象预先加 ACCESS SHARE 锁,以防对象中途被删除。如果此时有客户端请求排他锁,worker 会以 NOWAIT 方式尝试加锁;一旦失败说明有人正在申请排他锁,pg_dump 只能直接中止备份而不是陷入死锁。
内容选择:-a / -s / -b / -B
# 只导数据(表数据 + 大对象 + 序列值),不含定义与统计信息
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
# 只导某张表(可重复 -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
# -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 ...)和数据库级权限。
属主、权限与注释
# 不输出 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:
# 每行一条 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 是速度与兼容性的折中点。
一致性与等待控制
# 使用指定的导出快照(需预先用 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 只导单库」限制的一种手法。
编码与标识符
# 指定导出字符集
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,把过滤规则写进外部文件:
pg_dump --filter=filter.txt -f mydb.dump -Fc mydb该文件使用与 pg_restore --filter 相同的语法族:
# include / exclude + 对象类型 + 模式
include table public.orders_*
exclude table public.logs_*
include schema app相比于一长串 -t / -T 参数,过滤文件更易维护、可纳入版本控制。这是较新的特性,使用前请确认服务端与客户端工具版本都 ≥ 17。
权限要求与监控
导出所需的权限分两种情形:
- 超级用户:可以导出任意数据库;
- 普通用户:需要对被导出的表有
SELECT权限。如果只想给「能读全库」的能力而不授予超级用户,可以使用预定义角色pg_read_all_data:
-- 授予某账号读取所有数据的能力(PostgreSQL 14+)
GRANT pg_read_all_data TO backup_user;另外,pg_dump 需要读取系统目录,普通用户也需能读这些视图(默认可读)。如果备份的是 RLS 保护的表,注意 pg_dump 会以连接用户身份读数据,RLS 策略可能过滤掉不该丢的行——备份账号通常需要 BYPASSRLS 或属主身份。
监控进度用 --verbose:
pg_dump --verbose -Fc -f mydb.dump mydb 2> dump.log--verbose 会把详细的进度与耗时写到标准错误,便于判断卡在哪张表。
实战示例
示例一:单库日常备份(自定义格式)
#!/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"示例二:并行导出大库
# 目录格式 + 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 | headtotal 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示例三:跨版本导出并导入
# 源库是 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示例四:只导某几张表并单独恢复
# 导出两张表(含定义与数据)
pg_dump -t 'public.orders' -t 'public.order_items' \
-Fc -f /tmp/two_tables.dump orders
# 先看清单,确认对象
pg_restore -l /tmp/two_tables.dump;
; 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# 恢复到目标库(先建好目标库)
pg_restore -d target_db --no-owner -t orders -t order_items /tmp/two_tables.dump示例五:把表数据导出为可读 SQL
# 逐行 INSERT + 不导 owner/权限,便于人工审阅与修改
pg_dump --inserts --column-inserts -O --no-privileges \
-t 'public.orders' orders > orders_readable.sql
head -30 orders_readable.sql--
-- 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');示例六:多库绑定同一快照导出
# 在第一个会话里导出快照 ID,保持事务打开
psql -h 10.0.0.5 -U backup_user -d db_aBEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT pg_export_snapshot(); pg_export_snapshot
--------------------
00000003-0000002A-1
(1 row)# 保持上面的事务不提交,在其他终端用同一快照导出两个库
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 冲突。轻则排队等待,重则导致并行备份中止:
-- 备份期间执行这条可能让 pg_dump -j 直接失败
ALTER TABLE orders ADD COLUMN channel text;-c / -C / -O 在归档格式下会被忽略
# 错误预期:以为 -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」错误信息,看起来像失败,实际无碍:
pg_dump -c --if-exists -f mydb.sql mydb # 推荐组合-t 过滤出来的备份通常不能独立恢复
依赖的对象(序列、约束、索引、类型、函数)不会自动带上:
# 危险:单表导出可能缺少序列默认值,恢复后主键无法自增
pg_dump -t public.orders -f only_orders.sql mydb密码不要写在命令行或环境里
-W 提示输入或 .pgpass 才是安全做法。命令行参数对同机所有用户可见,PGPASSWORD 亦然:
# 不安全(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 之后,放大影响面。
最佳实践
- 生产库优先用
-Fc或-Fd归档格式,而不是默认的纯文本。归档格式支持选择性恢复、并行恢复、清单编辑,且默认压缩,是唯一能「只找回一张表」的备份形态。
pg_dump -Fc -Z zstd:9 -f /backup/${DB}_$(date +%F).dump "$DB"压缩算法按场景选。要备份速度选
lz4,要空间效率选zstd,纯归档冷存储可用zstd:9以上;gzip是兼容性最好但效率最低的默认项。大库用目录格式 +
-j并行,并预留连接数。-j 8需要额外的 8 条连接,务必确认max_connections有余量,否则会挤掉业务连接。跨环境搬运一律加
--no-owner --no-privileges(归档格式在恢复时加),避免目标环境缺角色导致的恢复失败:
pg_restore -d target --no-owner --no-privileges -j 4 /backup/app.dump- 备份脚本必须包含「可读性校验」环节。备份文件写成功不等于可用:
pg_restore -l "$DUMP" > /dev/null || { echo "archive corrupt: $DUMP"; exit 1; }定期做真实恢复演练。备份的唯一价值是能恢复,建议至少每月把最新备份恢复到临时实例并跑一次行数 / 校验和比对。
备份与 DDL 变更窗口错开。发布日、数据库迁移日不要让备份任务与 DDL 抢锁;必要时用
--lock-wait-timeout让备份主动失败而不是拖长窗口。
pg_dump --lock-wait-timeout=30s -Fc -f "$DUMP" "$DB" || echo "backup skipped: lock timeout"- 凭据用
.pgpass(0600)而不是PGPASSWORD,并给备份账号最小权限——推荐pg_read_all_data而非超级用户:
GRANT pg_read_all_data TO backup_user;明确备份账号与 RLS 的关系。备份 RLS 表时,若账号不带
BYPASSRLS又不是表属主,导出的数据会被策略过滤,产生「静默丢数据」的备份。用专用账号并显式确认。用
--verbose留日志,并记录耗时趋势。备份变慢往往是数据量增长或锁等待的信号:
pg_dump --verbose -Fc -f "$DUMP" "$DB" 2>> /var/log/pg-backup.log- 序列值必须显式确认已包含。默认会导出序列的
setval(),但配合-s(仅结构)时不会。恢复后先验证序列当前值,避免主键冲突:
SELECT last_value, is_called FROM orders_id_seq;- 给备份文件做保留策略与异地副本。本地保留近 7 天 / 4 周,异地或对象存储保存月度归档,避免单点故障与误删。
下一节
继续学习:pg_dumpall 全集群备份
参考链接
- PostgreSQL 官方文档:pg_dump
- PostgreSQL 官方文档:Backup and Restore
- PostgreSQL 官方文档:SQL Dump
- PostgreSQL 官方文档:pg_restore
- PostgreSQL 官方文档:pg_dumpall
- PostgreSQL 官方文档:Environment Variables(libpq)
- PostgreSQL 官方文档:The Password File
- PostgreSQL 官方文档:Explicit Locking
- PostgreSQL 官方文档:Predefined Roles
- PostgreSQL 官方文档:Transaction Isolation