在PostgreSQL的日常运维和开发工作中,经常需要把数据库的表结构和数据分开导出。举个典型的场景:团队要在测试环境重建一套和线上一致的库表结构,但不需要线上的真实数据;或者反过来,结构已经建好了,只需要把一批数据灌进去。如果结构和数据混在一个备份文件里,处理起来会非常别扭。pg_dump针对这类需求提供了完善的参数支持,本文详细介绍几种分开导出的方式以及恢复时的注意事项。

一、理解pg_dump中"模式"的含义
很多初学者容易把pg_dump文档里提到的schema和数据库表结构搞混。PostgreSQL中schema是指命名空间,比如默认的public schema;而日常说的"导出模式"通常指的是表结构定义,包括建表语句、字段类型、约束、索引、视图、函数、序列等DDL对象。
pg_dump的--schema-only参数对应的是后者,即只导出对象定义,不导出表中的数据。它的命名容易让人误解,但实际效果就是导出整个数据库的结构骨架。掌握了这个概念,后面的参数理解起来就顺畅了。
另外还要注意,--schema-only导出的内容包括序列的定义但不含序列的当前值,如果后续配合--data-only导入数据,需要额外考虑序列值同步的问题,这一点在后文会详细说明。
二、分开导出的核心参数用法
最基础的组合是两条命令分别执行。先导出结构,再导出数据:
# 只导出表结构,生成纯文本SQL文件 pg_dump -h 127.0.0.1 -U postgres -d mydb --schema-only -f schema.sql # 只导出数据,生成纯文本SQL文件 pg_dump -h 127.0.0.1 -U postgres -d mydb --data-only -f data.sql
结构文件中包含CREATE TABLE、CREATE INDEX、ALTER TABLE ADD CONSTRAINT等语句,数据文件中则是COPY或INSERT语句。恢复时先执行schema.sql建好结构,再执行data.sql灌入数据,顺序不能颠倒,否则数据没有地方可写。
如果希望得到更灵活的自定义格式文件,可以加-Fc参数。自定义格式必须配合pg_restore使用,好处是恢复时可以选择性导入:
# 导出自定义格式的结构备份 pg_dump -h 127.0.0.1 -U postgres -d mydb -Fc --schema-only -f schema.dump # 导出自定义格式的数据备份 pg_dump -h 127.0.0.1 -U postgres -d mydb -Fc --data-only -f data.dump # 恢复结构 pg_restore -h 127.0.0.1 -U postgres -d targetdb --clean --if-exists schema.dump # 恢复数据 pg_restore -h 127.0.0.1 -U postgres -d targetdb --disable-triggers data.dump
这里有个细节值得注意:--disable-triggers参数要求超级用户权限或者表的所有者权限,它的作用是在导入数据前临时禁用触发器,避免外键约束在数据未完全导入时报错。因为--data-only导出的数据默认按表名顺序导入,如果表之间存在外键依赖,先导入的表引用的后表数据可能还不存在,触发器一执行就会失败。
三、用--section参数做更细粒度的拆分
除了简单的结构和数据两分法,pg_dump还提供了--section参数,可以把备份内容拆成三段,这是很多人不知道的实用功能。三个段的含义分别是:
- pre-data:建表前的对象,包括表定义、类型、函数、序列创建等
- data:表中的实际数据,即COPY和INSERT语句
- post-data:建表后才能创建的对象,包括索引、外键约束、触发器、规则等
这样拆分的好处是恢复时可以先把所有表结构建好,再统一导入数据,最后统一创建索引和约束。这种顺序在大数据量场景下有明显优势,因为数据导入完成后再建索引,比边导入边维护索引快得多。
# 分三段导出 pg_dump -h 127.0.0.1 -U postgres -d mydb -Fc --section=pre-data -f pre.dump pg_dump -h 127.0.0.1 -U postgres -d mydb -Fc --section=data -f data.dump pg_dump -h 127.0.0.1 -U postgres -d mydb -Fc --section=post-data -f post.dump # 按顺序恢复到目标库 pg_restore -d targetdb pre.dump pg_restore -d targetdb --disable-triggers data.dump pg_restore -d targetdb post.dump
实际上--schema-only等价于同时导出pre-data和post-data两段,--data-only等价于只导出data段。理解了这个对应关系,就能明白为什么纯数据导入会遇到外键问题,而三段式恢复可以完美规避。
四、恢复时的常见问题与解决办法
第一个高频问题是序列值不同步。用--data-only导入数据后,表中数据已经有了,但自增序列的当前值还停留在初始状态。此时新插入一行会报主键冲突。解决办法是在导数据时加上--serializable-deferrable并不能解决问题,正确的做法是数据导入后手动执行setval重置序列,或者直接用包含结构的完整恢复流程,pg_dump在导出结构时会包含setval语句。
-- 手动重置序列的示例
SELECT setval('users_id_seq', (SELECT COALESCE(MAX(id), 1) FROM users));
第二个问题是权限归属。如果导出的库和目标库的用户名不同,纯文本SQL文件中的ALTER TABLE OWNER TO语句会执行失败。可以在pg_restore时加--no-owner参数跳过所有权设置,让对象归属执行恢复的用户。同理,--no-privileges可以跳过GRANT和REVOKE语句,这两个参数在做跨环境迁移时几乎是必加的。
第三个问题是格式选择。纯文本格式(默认)只能整体执行,不能用pg_restore做选择性恢复;自定义格式(-Fc)和目录格式(-Fd)支持并行恢复(-j参数),大数据量场景下速度提升明显。比如目录格式配合并行导出和并行恢复:
# 目录格式并行导出 pg_dump -h 127.0.0.1 -U postgres -d mydb -Fd -j 4 -f backup_dir # 并行恢复数据段,4个任务同时进行 pg_restore -d targetdb -j 4 --disable-triggers backup_dir
总结一下,分开导出的核心就是--schema-only、--data-only和--section三组参数的组合使用。小库用纯文本格式简单直接,大库建议用自定义格式或目录格式配合pg_restore,并结合三段式恢复顺序处理外键和索引问题。掌握这些方法后,无论是搭建测试环境、做版本管理还是跨库迁移数据,都能得心应手。
pg_dumpPostgreSQL导出模式导出修改时间:2026-09-10 01:40:34