pgbackrest 是一个惊人的备份工具,它坚如磐石(就像PostgreSQL一样),设计用于在繁重的数据库负载下工作。
而pgbackrest
的“异步”模式可以提高WAL的归档效率。
在“标准”模式下,pgbackrest
将使用PostgreSQL提供的archive_command
将WAL段推送到备份服务器。您可能已经知道,PostgreSQL将等待archive_command
完成并确认WAL的传输。可能发生以下情况:
archive_command
可能需要很长时间,虽然PostgreSQL将继续工作,但尚未转移的wal将使pg_wal
增长;archive_command
可能会失败,PostgreSQL将(在日志中)警告您此事件,并将再次尝试归档失败的WAL(直到成功)。
另一方面,在执行还原时,PostgreSQL执行restore_command
获取WAL段,这反过来会导致单个WAL请求运行pgbackrest
。
pgbackrest
通过“异步归档管理”的“推送”和“获取”,可以改善这种情况。这样做的目的是对pgbackrest
进行更多的控制,以便它可以优化输入/输出操作。
当PostgreSQL归档WAL段时,它会在循环中执行archive_command
:当WAL准备就绪时,会调用archive_command
,并且在它完成之前,不能归档其它的WAL段。另一方面,当PostgreSQL需要获取WAL以执行还原/恢复时,它会在希望重放的每个WAL段上执行restore_command
。因此,如果必须重放多个WAL,则必须依次执行restore_command
来“获取”每个WAL。
那么异步模式如何改进上述内容?
归档意味着推送,
pgbackrest
可以决定在一个批次中推送多个WAL,这意味着减少与备份服务器建立网络连接的操作。恢复意味着获取,
PGbackback
可以执行预取,在本地服务器上下载一些WAL,并在需要时立即将它们提供给PostgreSQL服务器。
测试环境
在这篇文章中,我将使用两台机器来演示pgbackrest
的异步用法:
miguel
是PostgreSQL 服务器,运行Fedora Linux和PostgreSQL 13.3;carmensita
是备份计算机,运行Fedora Linux。
pgbackrest
的版本为2.34
。
异步配置参数
配置参数可以在pgbackrest.conf
文件中配置,也可以像往常一样在命令行中指定。
异步参数设置主要考虑spool目录、队列和异步模式的启用。
异步模式的启用
只有一个配置参数可以启用异步模式:async
。默认是false。打开它,将自动使任何archive-get
和archive-push
处于异步模式。
spool目录
为了管理异步操作,pgbackrest
在PostgreSQL服务器上创建一个spool目录,通常是/var/spool/pgbackrest
,其中放置一个“archive”目录和一个以服务器命名的目录,或者更好的是以 stanza命名的目录。然后,可以将此类目录拆分为in
或out
,分别用于archive-get
和archive-push
。
可以使用spool-path
配置参数定义spool 目录。
例如,给定名为miguel
的stanza ,spool目录将是/var/spool/pgbackrest/archive/miguel/out
或/var/spool/pgbackrest/archive/miguel/in
。
系统将在out
目录中写入簿记内容,主要是小文本文件,用于确定归档的时间点。
在in
目录中,系统将存储准备对PostgreSQL服务器进行还原的WAL。
队列
有两种不同的设置来管理pgbackrest
的队列:
archive-push-max-queue
;archive-get-max-queue
.
它们的意义是配置在推送和获取操作时排队的数据的最大大小。当队列已满时,pgbackrest
将根据正在进行的操作进行不同的处理,如下所述。
配置
这台名为carmensita
的备份服务器有一个7etc/pgbackrest.conf
文件,配置如下:
$ cat etc/pgbackrest.conf
[global]
start-fast = y
stop-auto = y
repo1-path = backup/pgbackrest
repo1-retention-full=2
repo1-host-user = backup
log-level-console = info
[miguel]
pg1-host = miguel
pg1-path = postgres/13/data
复制
在PostgreSQL 服务器上,名为miguel
的/etc/pgbackrest.conf
文件
[global]
repo1-path = backup/pgbackrest
repo1-host-user = backup
log-level-console = info
repo1-host = carmensita
archive-async = y
archive-push-queue-max = 500MB
spool-path = var/spool/pgbackrest
archive-get-queue-max = 32MB
复制
最后,PostgreSQL 服务器上的archive_command
配置如下:
archive_command = '/usr/bin/pgbackrest \
--pg1-path=/postgres/13/data \
--config=/etc/pgbackrest.conf \
--stanza=miguel \
archive-push %p'
archive_mode = on
复制
请注意,archive-async
参数是在配置中指定的,而不是在archive-push
或archive-get
中设置的。在我看来,这简化了pgbackrest
的用法。
通过以上所有操作,可以了解异步模式的工作原理。
归档(archive-push)
让我们从备份场景开始,即archive-push
。
当一切进展顺利
让我们看看一切正常时会发生什么:我启动了一个pgbench
会话,以生成一些流量,从而生成一些WAL段并进行归档。pgbench
的运行方式如下:
% pgbench -c 8 -T 120 -h miguel -U pgbench -n -P 5 pgbench
复制
当pgbench
运行时,让我们检查一下PostgreSQL机器上发生了什么,特别是spool文件夹:
# ls -1s var/spool/pgbackrest/archive/miguel/out \
&& psql -h miguel -U postgres \
-c 'select last_archived_wal from pg_stat_archiver;' postgres
0 000000070000014E00000015.ok
last_archived_wal
--------------------------
000000070000014E00000015
# # after a while
# ls -1s var/spool/pgbackrest/archive/miguel/out \
&& psql -h miguel -U postgres \
-c 'select last_archived_wal from pg_stat_archiver;' postgres
0 000000070000014E00000016.ok
last_archived_wal
--------------------------
000000070000014E00000016
复制
如您所见,在spool目录中,将有一个空文件,以最后一个存档的WAL段命名,这是发送到备份计算机的最后一个段,后缀为.ok
。
在PostgreSQL日志中,pgbackrest
完成推送时会有一个通知(取决于您配置的日志级别):
INFO: pushed WAL file '000000070000014E000000AD' to the archive asynchronously
复制
当出现问题时
第一种情况:关闭备份计算机
假设备份服务器carmensita
已关闭。归档无法工作,如果您在PostgreSQL 服务器上再次生成一些流量(例如,使用如上所示的pgbench
),spool目录上的情况是:
# ls -1s var/spool/pgbackrest/archive/miguel/out \
&& psql -h miguel -U postgres \
-c 'select last_archived_wal, last_failed_wal from pg_stat_archiver;' postgres
4 global.error
last_archived_wal | last_failed_wal
--------------------------|--------------------------
000000070000014E0000001A | 000000070000014E0000001B
复制
global.error
文件包含对所发生情况的文本描述:
# cat var/spool/pgbackrest/archive/miguel/out/global.error
103
unable to find a valid repository:
repo1: [UnknownError] remote-0 process on 'carmensita' terminated unexpectedly [255]: ssh: connect to host carmensita port 22: No route to hos
复制
如果随后重新启动备份服务器,归档再次开始工作,则spool目录上的情况如下:
# ls -1s var/spool/pgbackrest/archive/miguel/out \
&& psql -h miguel -U postgres \
-c 'select last_archived_wal, last_failed_wal from pg_stat_archiver;' postgres
0 000000070000014E0000001B.ok
0 000000070000014E0000001C.ok
0 000000070000014E0000001D.ok
0 000000070000014E0000001E.ok
0 000000070000014E0000001F.ok
last_archived_wal | last_failed_wal
--------------------------|--------------------------
000000070000014E0000001F | 000000070000014E0000001B
复制
正如您所看到的,.ok
文件已经存在,归档工作又开始了。
在此期间,可能有一个或多个.ok
文件。最后一个.ok
文件表示最后一个异步存档的WAL段(在上面,是以1F
结尾的那个)。
第二种情况:生成了更多的WAL
再次关闭备份服务器,使PostgreSQL 服务器无法归档WAL段;然后生成大量流量以增加WAL目录的大小(pg_wal
)。
让我们检查一下情况:
# ls -1s var/spool/pgbackrest/archive/miguel/out \
&& psql -h miguel -U postgres \
-c 'select last_archived_wal, last_failed_wal from pg_stat_archiver;' postgres
4 global.error
last_archived_wal | last_failed_wal
--------------------------|--------------------------
000000070000014E000000ED | 000000070000014E000000EE
# cat var/spool/pgbackrest/archive/miguel/out/global.error
103
unable to find a valid repository:
repo1: [UnknownError] remote-0 process on 'carmensita' terminated unexpectedly [255]: ssh: connect to host carmensita port 22: No route to host
复制
因此,000000070000014E000000ED
是备份计算机上最后一个归档的WAL。
假设现在PostgreSQL上有大量数据被修改,因此它开始生成WAL段。显然,PostgreSQL无法再存档段,将开始把它们累积到pg_wal
中,以使它们在archive_command
再次开始工作时可用。spool目录中的情况会还是这样吗?让我们再次检查:
# ls -1s var/spool/pgbackrest/archive/miguel/out \
&& psql -h miguel -U postgres \
-c 'select last_archived_wal, last_failed_wal from pg_stat_archiver;' postgres
4 000000070000014F00000053.ok
4 000000070000014F00000054.ok
4 000000070000014F00000055.ok
4 000000070000014F00000056.ok
4 000000070000014F00000057.ok
4 000000070000014F00000058.ok
4 000000070000014F00000059.ok
4 000000070000014F0000005A.ok.pgbackrest.tmp
last_archived_wal | last_failed_wal
--------------------------|--------------------------
000000070000014F00000059 | 000000070000014F00000053
# cat /var/spool/pgbackrest/archive/miguel/out/000000070000014F00000059.ok
0
dropped WAL file '000000070000014F00000059' because archive queue exceeded 500MB
复制
首先:last_archived_wal
已经往前推了,即使acrhive_command
失败(请记住备份服务器已关闭)!这怎么可能?
答案是pgbackrest
的异步工作原理:如果失败的WAL数量大于指定的大小,pgbackrest
将“承认”归档到达备份服务器,即使归档的WAL没有到备份服务器。
其想法是,pgbackrest
将阻止pg_wal
无休止的增长,从而有可能阻止PostgreSQL的工作。但是,承认一个虚假归档意味着WAL流已被破坏,因此无法再围绕这个漏洞进行时间点恢复,因此强烈建议使用新的备份机器!
pgbackrest
将一个信息插入其.ok
文件中,该文件现在是非空的,并通知管理员WAL段已明确删除。
您可以在PostgreSQL日志中找到相同的信息,pgbackrest
会在日志中打印一条消息,以明确:
% sudo grep 000000070000014F00000059 $PGLOG
INFO: archive-push command begin 2.34: [pg_wal/000000070000014F00000059] --archive-async --archive-push-queue-max=500MB --config=/etc/pgbackrest.conf --exec-id=40124-af251b1c --log-level-console=info --pg1-path=/postgres/13/data --repo1-host=carmensita --repo1-host-user=backup --repo1-path=/backup/pgbackrest --spool-path=/var/spool/pgbackrest --stanza=miguel
WARN: dropped WAL file '000000070000014F00000059' because archive queue exceeded 500MB
INFO: pushed WAL file '000000070000014F00000059' to the archive asynchronously
复制
有三条信息:
pgbackrest
试图归档WAL段,失败;有一个
WARN
通知您,pgbackrest
检测PostgreSQL以删除WAL文件,就好像它已正确归档;pgbackrest
表示它已保存该归档文件,因此PostgreSQL可以继续删除或回收该文件。
pgbackrest
什么时候决定放弃并开始冒充PostgreSQL?acrhive-push-queue-max
配置确定了pgbackrest
归档失败的数据大小。在我的配置中archive-push-queue-max=500MB
,这意味着产生500MB
的堵塞之后,pgbackrest
将开始进行伪造,WAL流中将出现一个洞。这大致对应堵塞了32
个WAL日志。
并行进程
配置参数process-max
用于控制启动多少个push workers来为异步系统服务。假设在配置中有process-max = 4
,那么在WAL归档期间,您可以在进程列表中看到以下内容:
# pstree -c -A
systemd-|-NetworkManager-|-{NetworkManager}
...
|-pgbackrest-|-pgbackrest---ssh
| |-pgbackrest---ssh
| |-pgbackrest---ssh
| |-pgbackrest---ssh
| `-ssh
...
|-postmaster-|-postmaster
| |-postmaster
| |-postmaster
| |-postmaster
| |-postmaster
| |-postmaster---pgbackrest
| |-postmaster
| `-postmaster
...
复制
如您所见,PostgreSQL已启动了pgbackrest
(即正在执行archive_command
),共有四个pgbackrest
进程。如果系统以同步模式推送归档文件,则忽略process-max
参数。
每个并发进程将共享一个exec-id
,用于标识进程所属的批次:
# pstree -A -c -a -l | grep pgbackrest
...
|-pgbackrest --config=/etc/pgbackrest.conf --exec-id=46475-10e060a1
| |-pgbackrest --config=/etc/pgbackrest.conf --exec-id=46475-10e060a1
| |-pgbackrest --config=/etc/pgbackrest.conf --exec-id=46475-10e060a1
| |-pgbackrest --config=/etc/pgbackrest.conf --exec-id=46475-10e060a1
...
复制
还原 (archive-get)
让我们从最近的备份执行恢复:
% sudo systemctl stop postgresql-13.service
% sudo -u postgres pgbackrest --stanza miguel \
--pg1-path /postgres/13/data --delta restore
...
INFO: restore command end: completed successfully (69861ms)
复制
在还原过程中,pgbackrest
的spool目录中的archive
目录将被清理,尤其是特定的服务器目录miguel
将被删除,因为没有进行WAL归档。
postgresql.auto.conf
文件包含archive-get
命令,提取WAL段:
% sudo cat /postgres/13/data/postgresql.auto.conf
# Recovery settings generated by pgBackRest restore on 2021-07-27 05:26:26
restore_command = 'pgbackrest --pg1-path=/postgres/13/data --stanza=miguel archive-get %f "%p"'
复制
在系统启动期间,pgbackrest
将(像往常一样)从备份服务器获取WAL段,但这次是以异步方式:
INFO: archive-get command begin 2.34: [000000070000014F000000A4, pg_wal/RECOVERYXLOG] --archive-async --archive-get-queue-max=32MB --exec-id=42831-f4ada646 --log-level-console=info --pg1-path=/postgres/13/data --repo1-host=carmensita --repo1-host-user=backup --repo1-path=/backup/pgbackrest --spool-path=/var/spool/pgbackrest --stanza=miguel
INFO: found 000000070000014F000000A4 in the archive asynchronously
INFO: archive-get command end: completed successfully (713ms)
复制
以上是PostgreSQL日志的摘录。同时,spool目录中填充了服务器的in
子目录,在该目录中存储了传入的WAL,并等待PostgreSQL服务器重放:
% sudo ls -1s /var/spool/pgbackrest/archive/miguel/in
16384 000000070000014F000000A4.pgbackrest.tmp
复制
在这种情况下,archive-get-queue-max
参数可以指定预取WAL的大小:pgbackrest
将在后台处理目录中提取和存储的WAL段不超过指定数量。与推送配置不同,设置此参数并不意味着系统将丢弃WAL。
结论
异步模式可以通过批次和预取WAL段来帮助提高性能。但是,您需要了解这样一个事实,即如果归档积累了太多的数据,异步推送的WAL流可能会产生漏洞。
在我看来,这是一个好的特性,因为根据我的经验,PostgreSQL服务器会由于机器(或网络)故障而累积太多的WAL段(甚至消耗了所有存储)。毕竟,pgbackrest
可以确保备份存在,至少可以确保您的PostgreSQL服务器不会因为archive_command
失败而变成只读。