由 John Doe 四月 21, 2025
你需要快速地导入导出数据吗?现在,PostgreSQL 支持以非文本模式来导出数据了。
特性提交日志
pg_dumpall 的非文本模式及 pg_restore 的相应变化。
pg_dumpall 获得了一个新的-F/--format
选项,其含义与 pg_dump 中的相同。默认值是p
,表示纯文本格式。对于任何其他值,会创建一个目录,其中包含两个文件:globals.data
和map.dat
。第一个文件包含用于恢复全局数据的 SQL 语句,第二个文件包含从对象标识符(oid)到数据库名称的映射。它还会包含一个名为databases
的子目录,在该子目录中会以指定格式创建归档文件,并使用数据库 oid 命名。
在这些情况下,-f
参数是必需的。
如果 pg_restore 遇到包含globals.dat
但没有toc.dat
的目录,它会先恢复全局设置,然后恢复每个数据库。
pg_restore 获得了两个新选项:-g/--globals-only
,用于禁止恢复任何数据库;--exclude-database
,用于禁止恢复特定的数据库,其工作方式与 pg_dumpall 中的相同选项一致。
讨论:https://postgr.es/m/cb103623-8ee6-4ba5-a2c9-f32e3a4933fa@dunslane.net
示例
很多 PostgreSQL 用户不喜欢使用 pg_dumpall,原因在于它速度慢,而且无法选择他们想要恢复的内容。这是因为某种程度上,pg_dumpall 只是简单地以纯文本格式顺序运行普通的 pg_dump。
而现在,这一巨大的改进让它变得非常出色。
让我们获取一些对比数据。在该测试实例中有几个数据库:
select datname, pg_size_pretty( pg_database_size(oid) ) from pg_database order by pg_database_size(oid) desc;
datname | pg_size_pretty
-----------------+----------------
redrock_explain | 7325 MB
redrock | 238 MB
pgdoc | 22 MB
inst | 16 MB
template1 | 7774 kB
pgdba | 7694 kB
template0 | 7694 kB
postgres | 7694 kB
(9 rows)
其中一个数据库比其他数据库大得多。使用传统方式运行 pg_dumpall:
$ time pg_dumpall -f pgdump.base.sql
real 0m58.893s
user 0m2.892s
sys 0m28.562s
$ du -h pgdump.base.sql
32G pgdump.base.sql
$ time gzip -vk pgdump.base.sql
pgdump.base.sql: 88.4% -- created pgdump.base.sql.gz
real 6m31.129s
user 6m8.112s
sys 0m21.083s
$ du -h pgdump.base.sql.gz
3.7G pgdump.base.sql.gz
所以,将所有数据库转储到单个文件中大约需要一分钟,但文件非常大,达到了 32GB。压缩花费了 6.5 分钟,将文件大小减少到了 3.7GB。
为了完整起见,让我们进行一次传统方式的转储,但采用实时压缩:
$ time pg_dumpall | gzip - > pgdump.compress.pipe.sql.gz
real 6m27.469s
user 6m18.340s
sys 0m31.272s
$ du -h pgdump.compress.pipe.sql.gz
3.7G pgdump.compress.pipe.sql.gz
和上面的测试进行对比,有了一定的性能提升。
现在,让我们看看还能做些什么:
$ time pg_dumpall -Fc -f pgdump.Fc.dump
real 5m55.864s
user 5m38.110s
sys 0m11.658s
$ du -sh pgdump.Fc.dump
3.7G pgdump.Fc.dump
使用-Fc
选项进行转储,我们又稍微节省了一些时间。
此外,虽然我们无法简单地限制从哪个数据库加载哪些对象,但是可以使用 pg_restore 来加载,并使用它的新选项:
$ pg_restore --globals-only -f globals.sql pgdump.Fc.dump/
这样就可以只恢复全局的元数据(角色、组权限、表空间)。
甚至,我们还可以只恢复部分的数据库。pg_restore 有一个选项--exclude-database=...
。但是,怎么知道转储中都有哪些数据库呢?让我们来看看上面的转储中都有些什么:
$ find pgdump.Fc.dump/ -ls
94784725 4 drwx------ 3 postgres postgres 4096 Apr 15 11:39 pgdump.Fc.dump/
94784726 4 -rw-rw-r-- 1 postgres postgres 1751 Apr 15 11:45 pgdump.Fc.dump/global.dat
94784728 4 -rw-rw-r-- 1 postgres postgres 116 Apr 15 11:45 pgdump.Fc.dump/map.dat
94784727 4 drwx------ 2 postgres postgres 4096 Apr 15 11:45 pgdump.Fc.dump/databases
94784747 2348 -rw-rw-r-- 1 postgres postgres 2403266 Apr 15 11:45 pgdump.Fc.dump/databases/574053.dmp
94784744 24644 -rw-rw-r-- 1 postgres postgres 25233590 Apr 15 11:39 pgdump.Fc.dump/databases/571913.dmp
94784758 704 -rw-rw-r-- 1 postgres postgres 718709 Apr 15 11:45 pgdump.Fc.dump/databases/571987.dmp
94784745 3807448 -rw-rw-r-- 1 postgres postgres 3898822633 Apr 15 11:45 pgdump.Fc.dump/databases/16395.dmp
94784759 4 -rw-rw-r-- 1 postgres postgres 1056 Apr 15 11:45 pgdump.Fc.dump/databases/5.dmp
94784753 4 -rw-rw-r-- 1 postgres postgres 832 Apr 15 11:45 pgdump.Fc.dump/databases/574187.dmp
94784742 4 -rw-rw-r-- 1 postgres postgres 1561 Apr 15 11:39 pgdump.Fc.dump/databases/1.dmp
所以,这些文件名也没什么太大帮助。但是,有一个文件还是看起来很有用的:
$ cat pgdump.Fc.dump/map.dat
1 template1
571913 redrock
16395 redrock_explain
574053 inst
574187 pgdba
571987 pgdoc
5 postgres
根据这些名称,我们可以决定只恢复pgdba
数据库,通过排除其他数据库来实现:
$ pg_restore -C -f just.one.db.sql --exclude-database="redrock*|template*|inst|pgdoc" pgdump.Fc.dump/
虽然这样可行,但是不能进行反向选择:只恢复一个数据库,或者不恢复全局的元数据。但我认为这些问题是可以解决的。
如果想要恢复单个数据库,我们也可以从databases/
目录中恢复其中一个.dmp
文件,这些文件都是由pg_dump -Fc
产生的普通文件:
$ file pgdump.Fc.dump/databases/574187.dmp
pgdump.Fc.dump/databases/574187.dmp: PostgreSQL custom database dump - v1.16-0
$ pg_restore -l pgdump.Fc.dump/databases/574187.dmp
;
; Archive created at 2025-04-15 11:45:33 CST
; dbname: pgdba
; TOC Entries: 4
; Compression: gzip
; Dump Version: 1.16-0
; Format: CUSTOM
...
现在 pg_dumpall 也支持目录格式了,pg_dump 很早就支持这种格式,但是在 pg_dumpall 中,即使使用-Fd
选项,我们也无法使用并行处理,因为它没有--jobs
选项。在支持并行转储之前,-Fd
选项在 pg_dumpall 中可能用处不大。如果你想快速进行转储,对每个数据库使用pg_dump -Fd
,并搭配合适的-j N
选项,应该会是最好的方法。
非常不错的改进。感谢社区的所有相关人员。
参考
提交日志:https://git.postgresql.org/pg/commitdiff/1495eff7bdb0779cc54ca04f3bd768f647240df2