第二篇我们来看WAL文件逻辑、物理结构以及管理方法。
一、 事务日志和WAL文件
1. 命名规则
在逻辑上,pg用一个地址空间长度为8B的虚拟文件表示事务日志(最大可达16EB)。
pg中的事务日志默认切分为16 MB的文件,每个文件称为WAL段。pg 11开始,使用initdb命令初始化时可以使用 –wal-segsize选项 配置WAL段文件的大小。
黄框部分表示WAL文件名由24个字符组成(每个字符以十六进制数表示),命名规则如下:
- 前8位:timelineId,即时间线id(备份恢复篇会提到)
- 中间8位:WAL的逻辑id,每个逻辑id默认大小为 256*16M(逻辑段*日志大小),即每个逻辑id包含256个16M的物理WAL文件
- 最后8位:当前WAL文件是本逻辑id的第几个(逻辑段号),由于最大是256个,因此其实只要最后2位就可以表示。
例如下面这个文件,就表示时间线=1,逻辑id=0,是本逻辑id的第1个WAL文件
2. 查看当前日志与文件名
默认日志大小
postgres=# show wal_segment_size;
wal_segment_size
------------------
16MB
(1 行记录)
查看当前日志
postgres=# select pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
0/1893A00
(1 行记录)
转为WAL文件名
pg 10开始用 pg_walfile_name,10之前用 pg_xlogfile_name
postgres=# select pg_walfile_name('0/1893A00');
pg_walfile_name
--------------------------
000000010000000000000001
(1 行记录)
postgres=# select pg_walfile_name(pg_current_wal_insert_lsn());
pg_walfile_name
--------------------------
000000010000000000000001
(1 行记录)
二、 WAL的内部结构
默认情况下,WAL段是一个16 MB的文件,内部切分为8192字节(8K)的页面。第一个page包含由结构体XLogLongPageHeaderData定义的头数据,而其他的page包含结构体XLogPageHeaderData定义的头数据。在页头之后,则是以降序写入page的XLOG记录。
XLogLongPageHeaderData和XLogPageHeaderData结构体在src/include/access/xlog_internal.h中定义。这里省略了对这两种结构体的说明,因为在以下描述中不需要这些结构。
typedef struct XLogPageHeaderData
{
uint16 xlp_magic; /* 用于正确性检查的魔数(magic value) */
uint16 xlp_info; /* 标记位 */
TimeLineID xlp_tli; /* 页面中第一条记录的时间线id */
XLogRecPtr xlp_pageaddr; /* 当前页面的clog地址 */
/*
* When there is not enough space on current page for whole record, we
* continue on the next page. xlp_rem_len is the number of bytes
* remaining from a previous page.
*
* Note that xlp_rem_len includes backup-block data; that is, it tracks
* xl_tot_len not xl_len in the initial header. Also note that the
* continuation data isn't necessarily aligned.
*/
uint32 xlp_rem_len; /* total len of remaining data for record */
} XLogPageHeaderData;
#define SizeOfXLogShortPHD MAXALIGN(sizeof(XLogPageHeaderData))
typedef XLogPageHeaderData *XLogPageHeader;
/*
* When the XLP_LONG_HEADER flag is set, we store additional fields in the
* page header. (This is ordinarily done just in the first page of an
* XLOG file.) The additional fields serve to identify the file accurately.
*/
typedef struct XLogLongPageHeaderData
{
XLogPageHeaderData std; /* standard header fields */
uint64 xlp_sysid; /* system identifier from pg_control */
uint32 xlp_seg_size; /* just as a cross-check */
uint32 xlp_xlog_blcksz; /* just as a cross-check */
} XLogLongPageHeaderData;
#define SizeOfXLogLongPHD MAXALIGN(sizeof(XLogLongPageHeaderData))
typedef XLogLongPageHeaderData *XLogLongPageHeader;
三、 WAL段文件切换与管理
PostgreSQL将XLOG记录写入在pg_xlog目录(10开始为pg_wal目录)的WAL段文件中。旧文件写满时则切换到新文件。 WAL文件的数量由参数配置而定。
1. WAL文件切换
发生以下任一情况时,会发生WAL文件切换:
- WAL文件已经写满
- 执行pg_switch_xlog()函数,10后为pg_switch_wal()
- 启用archive_mode且超过archive_timeout设置值
切换后的段文件通常会被回收(重命名或重用)以供将来使用,如果不需要,也可能会被删除。
2. 9.5开始的WAL段管理
每当检查点启动时,PostgreSQL都会预估并准备下一个检查点周期所需的WAL段文件数。这种估计基于前一个检查点周期中消耗的文件数量,即从包含上一个重做点的段文件开始计数,这个值范围在min_wal_size(默认80 MB,即5个文件)和max_wal_size(默认1GB,即64个文件)之间。如果检查点进程启动,必要的文件会被保留或回收,不必要的文件会被删除。
具体例子如下图,假设在检查点开始前有6个文件,WAL_3包含了上一个重做点(11之前版本为上一个重做点,11开始为当前重做点),pg预估需要5个文件。在这种情况下,WAL_1被重命名为WAL_7回收利用,而WAL_2将被删除。
任何比包含上一个重做点的WAL文件更旧的WAL文件都可以被删除,因为它们在崩溃恢复时已不会被用到
如果出现了WAL活动尖峰,需要更多WAL文件,新的文件就会被创建,但WAL文件的总大小不能超过max_wal_size的值。
例如下图,如果WAL_7已填充满,则新创建WAL_8。
WAL文件的数量会根据数据库的繁忙程度自适应地改变。如果WAL数据写入量不断增加,则WAL文件的预估数及WAL文件的总大小也会逐渐增加。反之则减少。
如果WAL文件的总大小超过max_wal_size,将启动检查点,如下图。检查点将会创建一个新的重做点,之前最新的重做点将变为前一个重做点,不必要的文件会被回收。通过这种方式,pg将始终只保留崩溃恢复必须的WAL文件。配置参数wal_keep_segments和复制槽功能也会影响WAL段文件的数量。
四、 持续归档和归档日志
持续归档是当WAL文件发生切换时自动将其复制至归档目录的一项功能,归档由archiver process执行,复制出来的文件称为归档日志。此功能常用于物理热备份和PITR(时间点恢复)。
归档目录的路径设置由archive_command参数配置
# %p被复制WAL文件目录占位符,%f是归档日志文件的占位符
archive_command = 'cp %p /home/postgres/archives/%f'
在WAL_7文件发生切换时,该文件作为 归档日志7 被复制到归档目录。
archive_command可以配置为任何Unix命令或程序,因此也可以通过scp命令将归档日志传输到其他主机,或使用任意的文件备份工具来代替普通复制命令。
pg不会自动清理归档日志,因此打开归档时需要妥善管理。如你什么都不做,归档日志的数量将不断增加。pg_archivecleanup是一个实用的归档日志管理工具。
参考
The Internals of PostgreSQL : Chapter 9 Write Ahead Logging — WAL
https://github.com/postgres/postgres/blob/master/src/include/access/xlog_internal.h