由 John Doe 七月 10, 2025
你在使用 PostgreSQL 的增量备份吗?那你想知道增量备份相关的运行状态吗?
特性提交日志
新增函数 pg_get_wal_summarizer_state()。
这使得从 SQL 中获取 WAL 汇总进度信息成为可能。此前新增的函数pg_available_wal_summaries()
和pg_wal_summary_contents()
仅检查磁盘状态,而本函数则公开了来自服务器共享内存的信息。
讨论:http://postgr.es/m/CA+Tgmobvqqj-DW9F7uUzT-cQqs6wcVb-Xhs=w=hzJnXSE-kRGw@mail.gmail.com
示例
下面,我们将介绍随着增量备份特性新增的系统信息函数。
我们先从一个启用了"summarize_wal"的全新集群开始:
postgres=# \dconfig *summa*
List of configuration parameters
Parameter | Value
-----------------------+-------
summarize_wal | on
wal_summary_keep_time | 10d
(2 rows)
WAL 摘要会写入pg_wal/summaries
目录,当然,由于我们尚未对该实例进行任何操作,目前这里看不到任何内容:
$ ls -la $PGDATA/pg_wal/
total 16400
drwx------ 4 postgres postgres 4096 Jun 18 10:51 .
drwx------ 20 postgres postgres 4096 Jun 18 10:51 ..
-rw------- 1 postgres postgres 16777216 Jun 18 10:53 000000010000000000000001
drwx------ 2 postgres postgres 4096 Jun 18 10:51 archive_status
drwx------ 2 postgres postgres 4096 Jun 18 10:51 summaries
$ ls -la $PGDATA/pg_wal/summaries
total 44
drwx------ 2 postgres postgres 4096 Jun 18 10:51 .
drwx------ 4 postgres postgres 4096 Jun 18 10:51 ..
-rw------- 1 postgres postgres 4808 Jun 18 10:51 00000001000000000100002800000000010B3098.summary
-rw------- 1 postgres postgres 4944 Jun 18 10:51 0000000100000000010B309800000000014E8838.summary
-rw------- 1 postgres postgres 32 Jun 18 10:51 0000000100000000014E883800000000014E8938.summary
-rw------- 1 postgres postgres 230 Jun 18 10:51 0000000100000000014E893800000000014EF360.summary
-rw------- 1 postgres postgres 32 Jun 18 10:51 0000000100000000014EF36000000000014EF460.summary
-rw------- 1 postgres postgres 88 Jun 18 10:51 0000000100000000014EF46000000000014EF798.summary
-rw------- 1 postgres postgres 32 Jun 18 10:51 0000000100000000014EF79800000000014EF810.summary
事实并非如此,尽管从用户角度来看没有进行任何操作,但我们确实看到这里有不少文件。这正是介绍随着增量备份特性新增的三个系统信息函数的绝佳场景。我们先来看pg_available_wal_summaries()
,它能为我们呈现与文件系统相同的情况,还附带额外信息:
select * from pg_available_wal_summaries();
tli | start_lsn | end_lsn
-----+-----------+-----------
1 | 0/14EF360 | 0/14EF460
1 | 0/14E8938 | 0/14EF360
1 | 0/1000028 | 0/10B3098
1 | 0/14EF460 | 0/14EF798
1 | 0/14E8838 | 0/14E8938
1 | 0/10B3098 | 0/14E8838
1 | 0/14EF798 | 0/14EF810
(7 rows)
这看起来有点奇怪,因为我们看不到各行之间有任何关联。换句话说:按常理,这不应该是一个链状结构吗?一行的 end_lsn 应该是下一行的 start_lsn 才对?由于我们处于同一时间线(tli),按 start_lsn 排序后,我们得到了预期的结果:
select * from pg_available_wal_summaries() order by start_lsn;
tli | start_lsn | end_lsn
-----+-----------+-----------
1 | 0/1000028 | 0/10B3098
1 | 0/10B3098 | 0/14E8838
1 | 0/14E8838 | 0/14E8938
1 | 0/14E8938 | 0/14EF360
1 | 0/14EF360 | 0/14EF460
1 | 0/14EF460 | 0/14EF798
1 | 0/14EF798 | 0/14EF810
(7 rows)
现在我们看到的情况与文件系统一致,例如,第一行的 end_lsn 是下一行的 start_lsn,并以此类推,直至摘要的末尾:
$ ls -la $PGDATA/pg_wal/summaries
total 44
drwx------ 2 postgres postgres 4096 Jun 18 10:51 .
drwx------ 4 postgres postgres 4096 Jun 18 10:51 ..
-rw------- 1 postgres postgres 4808 Jun 18 10:51 00000001000000000100002800000000010B3098.summary
-rw------- 1 postgres postgres 4944 Jun 18 10:51 0000000100000000010B309800000000014E8838.summary
-rw------- 1 postgres postgres 32 Jun 18 10:51 0000000100000000014E883800000000014E8938.summary
-rw------- 1 postgres postgres 230 Jun 18 10:51 0000000100000000014E893800000000014EF360.summary
-rw------- 1 postgres postgres 32 Jun 18 10:51 0000000100000000014EF36000000000014EF460.summary
-rw------- 1 postgres postgres 88 Jun 18 10:51 0000000100000000014EF46000000000014EF798.summary
-rw------- 1 postgres postgres 32 Jun 18 10:51 0000000100000000014EF79800000000014EF810.summary
磁盘上的文件名也是这样构成的:先是起始和结束时间线,接着是起始和结束的 lsn(日志序列号)。
接下来我们可以看看pg_wal_summary_contents( tli bigint, start_lsn pg_lsn, end_lsn pg_lsn )
函数。这个函数能让我们可以了解摘要中包含的内容,例如,使用上面的第一个条目:
select * from pg_wal_summary_contents( 1, '0/1000028', '0/10B3098' );
relfilenode | reltablespace | reldatabase | relforknumber | relblocknumber | is_limit_block
-------------+---------------+-------------+---------------+----------------+----------------
112 | 1663 | 1 | 0 | 0 | f
113 | 1663 | 1 | 0 | 0 | f
174 | 1663 | 1 | 0 | 0 | f
175 | 1663 | 1 | 0 | 0 | f
548 | 1663 | 1 | 0 | 0 | f
549 | 1663 | 1 | 0 | 0 | f
...
这意味着什么呢?首先,这与 template1 数据库有关:
select oid, datname from pg_database;
oid | datname
-----+-----------
5 | postgres
1 | template1
4 | template0
(3 rows)
表空间是 pg_default:
select oid, spcname from pg_tablespace ;
oid | spcname
------+------------
1663 | pg_default
1664 | pg_global
(2 rows)
relfilenode 列描述的是磁盘上的文件,例如:
$ ls -la $PGDATA/base/1/112*
-rw------- 1 postgres postgres 8192 Jun 18 10:51 /db/pgsql/data/base/1/112
relblocknumber 表示已更改的块。is_limit_block 的含义是:每个 is_limit_block 为 false 的行表示,由其余输出列标识的块在本文件所总结的记录范围内被至少一个 WAL 记录修改过。每个 is_limit_block 为 true 的行表示,要么(a)在相关的 WAL 记录范围内,关系分支被截断到 relblocknumber 所给出的长度;要么(b)在相关的 WAL 记录范围内,关系分支被创建或删除;在这种情况下,relblocknumber 将为零。
让我们做一个简单的测试,看看这到底意味着什么。我们创建一个包含一行数据的简单表:
create table t ( a int );
insert into t values (1);
select pg_relation_filepath('t');
pg_relation_filepath
----------------------
base/5/16388
(1 row)
如果我们查询最后一个摘要文件,应该会在pg_wal_summary_contents
的输出中看到一个新条目:
select * from pg_wal_summary_contents( 1, '0/14EF810', '0/1557C00' ) where relfilenode = 16388;
relfilenode | reltablespace | reldatabase | relforknumber | relblocknumber | is_limit_block
-------------+---------------+-------------+---------------+----------------+----------------
(0 rows)
我们什么也没看到,原因是新的摘要文件尚未写入,让我们强制执行检查点,并再次检查:
checkpoint;
select * from pg_available_wal_summaries() order by start_lsn;
tli | start_lsn | end_lsn
-----+-----------+-----------
1 | 0/1000028 | 0/10B3098
1 | 0/10B3098 | 0/14E8838
1 | 0/14E8838 | 0/14E8938
1 | 0/14E8938 | 0/14EF360
1 | 0/14EF360 | 0/14EF460
1 | 0/14EF460 | 0/14EF798
1 | 0/14EF798 | 0/14EF810
1 | 0/14EF810 | 0/1557C00
1 | 0/1557C00 | 0/15598C8
(9 rows)
select * from pg_wal_summary_contents( 1, '0/1557C00', '0/15598C8' ) where relfilenode = 16388;
relfilenode | reltablespace | reldatabase | relforknumber | relblocknumber | is_limit_block
-------------+---------------+-------------+---------------+----------------+----------------
16388 | 1663 | 5 | 0 | 0 | f
(1 row)
让我们添加更多数据,看看这在 WAL 摘要中是如何记录的:
insert into t select * from generate_series(1, 1000000);
checkpoint;
select * from pg_available_wal_summaries() order by start_lsn;
tli | start_lsn | end_lsn
-----+-----------+-----------
1 | 0/1000028 | 0/10B3098
1 | 0/10B3098 | 0/14E8838
1 | 0/14E8838 | 0/14E8938
1 | 0/14E8938 | 0/14EF360
1 | 0/14EF360 | 0/14EF460
1 | 0/14EF460 | 0/14EF798
1 | 0/14EF798 | 0/14EF810
1 | 0/14EF810 | 0/1557C00
1 | 0/1557C00 | 0/15598C8
1 | 0/15598C8 | 0/5290968
(10 rows)
select * from pg_wal_summary_contents(1, '0/15598C8', '0/5290968') where relfilenode = 16388;
relfilenode | reltablespace | reldatabase | relforknumber | relblocknumber | is_limit_block
-------------+---------------+-------------+---------------+----------------+----------------
16388 | 1663 | 5 | 0 | 0 | f
16388 | 1663 | 5 | 0 | 1 | f
16388 | 1663 | 5 | 0 | 2 | f
16388 | 1663 | 5 | 0 | 3 | f
16388 | 1663 | 5 | 0 | 4 | f
16388 | 1663 | 5 | 0 | 5 | f
16388 | 1663 | 5 | 0 | 6 | f
...
16388 | 1663 | 5 | 0 | 4420 | f
16388 | 1663 | 5 | 0 | 4421 | f
16388 | 1663 | 5 | 0 | 4422 | f
16388 | 1663 | 5 | 0 | 4423 | f
16388 | 1663 | 5 | 0 | 4424 | f
(4425 rows)
这是否意味着我们修改了该表中的 4424 个块?是的,确实如此:
select pg_relation_filepath('t');
pg_relation_filepath
----------------------
base/5/16388
(1 row)
$ ls -la $PGDATA/base/5/16388*
-rw------- 1 postgres postgres 36249600 Jun 18 11:23 /db/pgsql/data/base/5/16388
-rw------- 1 postgres postgres 32768 Jun 18 11:23 /db/pgsql/data/base/5/16388_fsm
-rw------- 1 postgres postgres 8192 Jun 18 11:23 /db/pgsql/data/base/5/16388_vm
select 36249600/8192;
?column?
----------
4425
(1 row)
增量备份需要包含自上次完整备份以来发生更改的所有这些块。
最后一个函数给出了 WAL 摘要进程的状态:
select * from pg_get_wal_summarizer_state();
summarized_tli | summarized_lsn | pending_lsn | summarizer_pid
----------------+----------------+-------------+----------------
1 | 0/77DC3C0 | 0/77DE570 | 1638
(1 row)
非常简单:时间线、已写入摘要的最后一个 LSN,和 pending_lsn 表示 WAL 摘要进程已使用的最后一个 lsn。
非常不错的特性,感谢所有参与的社区人员。
参考
提交日志:https://git.postgresql.org/pg/commitdiff/d9ef650fca7bc574586f4171cd929cfd5240326e