PostgreSQL 15: 通过加载模块进行 WAL 归档

John Doe 六月 24, 2025

你有没有需要自定义 WAL 归档流程?比如:压缩、上传到指定备份目标,记录归档文件的校验码。

非洲肯尼亚草原上的一头大象

特性提交日志

允许通过可加载模块进行归档。

为每个要归档的 WAL 文件运行 shell 命令,会带来大量开销,并且可能无法提供所需的错误检查或确切语义。因此,我们提供了一个选项,即针对每个要归档的文件调用可加载模块,而不是运行 shell 命令。

此外,添加了一个 ‘basic_archive’ 的 contrib 模块作为示例实现,用于将 WAL 文件归档到本地目录。

讨论:http://postgr.es/m/20220202224433.GA1036711@nathanxps13

示例

通常,为了备份的目的,以及在流复制严重滞后时还能继续同步,需要启用 WAL 归档。

在之前的 PostgreSQL 版本中,要实现 WAL 归档,我们会使用 archive_command 设置,该设置基本上会为每个要归档的 WAL 文件运行一次 shell 命令。这虽然可行,但存在一些性能方面的缺点。如果生成大量 WAL,频繁创建新进程的成本不可忽视。

大多数常用工具通过一次处理多个文件来解决这个问题,并对不需要处理的文件 “标记” 为已归档。这虽然可行,但始终不够规范。 现在,新的 PostgreSQL 版本中,我们可以编写扩展,该扩展会针对每个 WAL 文件被调用,并且一直都是加载的。与创建新进程相关的所有开销都消失了。

让我们来看看它是如何工作的。首先,我们现在有了一个新的配置参数:archive_library。如果设置了该参数(并且 archive_mode 为 on),将会使用该库进行归档,而不是 archive_command。

让我们尝试设置它:

select name, setting from pg_settings  where name  ~ '^archive' order by 1;
          name           |  setting  
-------------------------+-----------
 archive_cleanup_command | 
 archive_command         | /bin/true
 archive_library         | 
 archive_mode            | on
 archive_timeout         | 0
(5 rows)

目前,我们没有设置归档。虽然已启用,但 archive_command 设置为 /bin/true,因此实际上没有进行归档。

让我们使用提供的示例归档库 basic_archive 来启用它:

alter system set archive_library = 'basic_archive';

可以通过执行pg_ctl reload或者select pg_reload_conf(),使更改生效。

请注意,我们还没有为归档指定任何目录。

通常,如果使用了 archive_command 但未正确设置,会看到类似这样的 postgres 进程:

USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
redrock     1189  0.0  0.0 219572  6272 ?        Ss   14:49   0:00 postgres: archiver process failed on 0000000100000F72000000F0

但现在,使用 archive_library 时,会是这样:

$ ps uw -U redrock  | grep arch
redrock      37562  0.0  0.0 209756  4964 ?        Ss   15:48   0:00 postgres: archiver

在上面没有看到任何错误。这要么意味着 ps 输出中不再显示相关信息,要么意味着它工作正常?

幸运的是,我们可以直接查询归档状态:

select * from pg_stat_archiver;
 archived_count |    last_archived_wal     |      last_archived_time       | failed_count | last_failed_wal | last_failed_time |          stats_reset          
----------------+--------------------------+-------------------------------+--------------+-----------------+------------------+-------------------------------
            233 | 0000000100000000000000E9 | 2025-02-06 15:48:06.802869+08 |            0 | [null]          | [null]           | 2025-02-06 14:57:31.437907+08
(1 row)

归档状态也是正常的。那么,归档正常工作了吗?让我们再检查下日志:

$ grep archive postgresql.log | grep -v pg_stat_archiver | tail -n 5
2025-02-06 15:50:46.500 CST @ 37562  WARNING:  archive_mode enabled, yet archiving is not configured
2025-02-06 15:51:46.561 CST @ 37562  WARNING:  archive_mode enabled, yet archiving is not configured
2025-02-06 15:52:46.581 CST @ 37562  WARNING:  archive_mode enabled, yet archiving is not configured
2025-02-06 15:53:46.641 CST @ 37562  WARNING:  archive_mode enabled, yet archiving is not configured
2025-02-06 15:54:46.648 CST @ 37562  WARNING:  archive_mode enabled, yet archiving is not configured

实际上,归档还是失败了。让我们来完成配置,首先,我们看看当前的 WAL 位置:

select pg_current_wal_lsn(), pg_walfile_name(pg_current_wal_lsn());
 pg_current_wal_lsn |     pg_walfile_name      
--------------------+--------------------------
 1/1206FCD0         | 000000010000000100000012
(1 row)

上面是当前的 WAL 位置。请注意,在 pg_stat_archiver 中我们看到 last_archived_wal 是 0000000100000000000000E9。

让我们来看看如何解决这个问题。我们先创建好目录/tmp/archive,并将basic_archive.archive_directory更改为/tmp/archive

重新加载配置,并等待一段时间后,查询状态如下:

select * from pg_stat_archiver ;
 archived_count |    last_archived_wal     |      last_archived_time       | failed_count | last_failed_wal | last_failed_time |          stats_reset          
----------------+--------------------------+-------------------------------+--------------+-----------------+------------------+-------------------------------
            273 | 000000010000000100000011 | 2025-02-06 16:00:25.272431+08 |            0 | [null]          | [null]           | 2025-02-06 14:57:31.437907+08
(1 row)

并且在/tmp/archive中,会看到有 40 个文件,从 0000000100000000000000EA 到 000000010000000100000011。

终于,归档正常工作了。

basic_archive 的代码非常简单,没有太多内容,它只是一个概念验证,证明它是可行的,以及它能实现什么。相信,Barman、pgBackRest 和其他归档或备份工具很快会发布新版本,这些版本可以提供归档库使用,而不是通过创建新进程来执行命令。

此外,一旦 WAL 归档真正工作起来以后,ps 输出中的信息也正常了:

redrock    37562  0.0  0.0 209756  6916 ?        Ss   15:48   0:00  \_ postgres: archiver last was 000000010000000100000011

非常不错的新特性,感谢社区的所有相关人员。

参考

提交日志:https://git.postgresql.org/pg/commitdiff/5ef1eefd76f404ddc59b885d50340e602b70f05f