导读:
一、Wal文件的page结构
数据库运行时在数据目录下的pg_wal目录(pg10.0以上版本)下,产生wal日志文件段(如000000010000000000000003),每一个wal段的page的构成如下图。
PageHeader
在wal page的组成中有两种pageheader结构,XLogPageHeaderData和XLogLongPageHeaderData。
可以看出实际上XLogLongPageHeaderData比XLogPageHeaderData多出三个成员。XLogLongPageHeaderData是用于一个segment的第一个page,xlp_sysid用于记录产生segment的数据库的id,xlp_seg_size和xlp_xlog_blcksz都是固定大小,分别为segment的size和page的size。
remaindata
这个数据块存储着上一个page的最后一个record没有存完的数据。他的大小是xlp_rem_len。当xlp_rem_len为0时,这个个数据块也就不存在了。
Record
参照下文中的wal record结构。
不完整的record
与remaindata相对应,自行理解。
无数据区域
一个记录里的XlogRecord结构是不能跨页存储的。因此,当剩余的空间不能存储一个XLogRecord结构体时就会被舍弃。
二、Wal记录record的结构
每一个wal记录Record的结构如下图所示。注意蓝框部分是数据描述结构,棕色框部分是实际保存的数据。
1.XLogRecord
XLogRecord是一个wal记录的入口,在解析wal记录时,将从这个结构体开始入手。如下是XlogRecord的结构体定义。
typedef struct XLogRecord
{
uint32 xl_tot_len;
TransactionId xl_xid;
XLogRecPtr xl_prev;
uint8 xl_info;
RmgrId xl_rmid;
pg_crc32c xl_crc;
} XLogRecord;
各成员的意义为:
xl_tot_len:这个记录的总长度,包括图所有的模块。
xl_xid:产生此记录的事务ID。
xl_prev:前一个记录的位置。
xl_rmid:此成员标志着是何种类型的wal记录,例如RM_XACT_ID为事务相关的记录、 RM_DBASE_ID 为数据库创建删除的记录、RM_HEAP_ID为表数据增删改相关记录。它的取值范围在src/include/access/rmgrlist.h文件中可以看到。
xl_info:此成员标志着是何种子类型的wal记录。xl_info与xl_rmid结合使用,例如xl_rmid为RM_HEAP_ID,那么xl_info可以为 XLOG_HEAP_INSERT、XLOG_HEAP_DELETE、XLOG_HEAP_UPDATE。
xl_crc:校验位。
2.BLOCK
(1) XLogRecordBlockHeader
在Record结构图中虚线框中的部分可以称为是一个Block的数据,这里主要描述buff相关的数据的结构。
这部分的结构是通过XLogRegisterBuffer()函数注册到wal记录中的(详见《postgres预写式日志的内核实现详解-wal记录写入》)。
typedef struct XLogRecordBlockHeader
{
uint8 id;
uint8 fork_flags;
uint16 data_length;
} XLogRecordBlockHeader;
各成员的意义为:
id:一个记录中可以有多个block(MAX: 32),此id是block的序号。
fork_flags: 本block存储有哪些信息,这个成员决定着①③结构和⑤数据是否存在。
data_length:决定⑤中存储的数据的长度。
(2) XLogRecordBlockImageHeader
这个结构体存在说明这个wal记录是一个fpw记录,这个结构体是wal记录page数据的
typedef struct XLogRecordBlockImageHeader
{
uint16 length;
uint16 hole_offset;
uint8 bimg_info;
/*
* If BKPIMAGE_HAS_HOLE and BKPIMAGE_IS_COMPRESSED, an
* XLogRecordBlockCompressHeader struct follows.
*/
} XLogRecordBlockImageHeader;
各成员的意义为:
length:保存的page的总长度(压缩、去零后)。
hole_offset: 零数据之前的数据的size。
bimg_info:标志位,这里决定了②结构是否存在
(3) XLogRecordBlockCompressHeader
此结构记录零数据的size
typedef struct XLogRecordBlockCompressHeader
{
uint16 hole_length; /* number of bytes in "hole" */
} XLogRecordBlockCompressHeader;
(4) RelFilenode
此结构记录了此block所属的关系。如果当前block与上一个block来源于同一个文件那么fork_flags中就不会有BKPBLOCK_SAME_REL标志位
那么此block中就不会有Relfilenode结构.
typedef struct RelFileNode
{
Oid spcNode; /* tablespace */
Oid dbNode; /* database */
Oid relNode; /* relation */
} RelFileNode;
(5) BlockNumber
记录此block记录的page的块号。
3.RepOriginId
代码中没有找到描述这个结构的结构体,这里dummy一个来说明这个结构。
typedef struct RepOriginDummy
{
uint8 id;/*XLR_BLOCK_ID_ORIGIN*/
RepOriginId reporiginid;
}RepOriginDummy;
4.XLogRecordDataHeaderLong/XLogRecordDataHeaderShort
此结构XLogRecordDataHeaderLong和XLogRecordDataHeaderShort互斥出现,当⑥的size大于255时使用XLogRecordDataHeaderLong结构
否则使用XLogRecordDataHeaderShort结构
typedef struct XLogRecordDataHeaderShort
{
uint8 id; /* XLR_BLOCK_ID_DATA_SHORT */
uint8 data_length;
} XLogRecordDataHeaderShort;
typedef struct XLogRecordDataHeaderLong
{
uint8 id;/* XLR_BLOCK_ID_DATA_LONG */
/* followed by uint32 data_length, unaligned */
} XLogRecordDataHeaderLong;
5.buffdata
record的结构图中的绿框部分是buffdata,包含pagedata和tupledata两部分。pgdata数据由XLogRegisterBuffer()函数注册到wal记录
tupledata数据由XLogRegisterBufData()函数注册到wal记录。这里存储了实际的buff数据和变更数据。
比如这是一个insert语句产生的的wal日志,pgdata可能保存了插入的目标page的备份。tupledata可能保存了插入的tuple数据。
6. main data
main data部分保存非buff性的数据,通过XLogRegisterData()函数注册的数据,比如wal子类型的特殊结构体、delete和update语句的旧元组或key