PostgreSQL-xlog

一、xlog存储结构

​ 每个XLOG文件有一个ID,一个逻辑上的xlog文件物理上被分割成一个个固定大小的段(默认16MB)。xlog文件号和段号可以唯一确定某个段文件。确定日志文件内的一个日志记录的地址时,只需要xlog文件号和日志记录在该文件内的偏移量即可。

​ 对于每一个Xlog文件第一个段的第一个页面是一个长头部(Long Header),一个xlog文件头部是不是长头部,可以由其头部XLogPageHeaderData的标志位xlp_info确定出来,如果是长头部,则:
XLogPageHeader page;
page->xlp_info = XLP_LONG_HEADER;

1、xlog日志页面头部信息

​ xlog日志文件分为许多的逻辑段,每一个段文件又分成许多个页面,每一个页面大小为一个块的大小。对于每一个日志页面,需要在其头部写一个头部信息XLogPageHeaderData,其结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define XLOG_PAGE_MAGIC 0xD093	/* can be used as WAL version indicator */

typedef struct XLogPageHeaderData
{
uint16 xlp_magic; /* 用于正确性检查的魔数 */
uint16 xlp_info; /* 标记位 */
TimeLineID xlp_tli; /* 页面中第一条记录的时间线ID */
XLogRecPtr xlp_pageaddr; /* 当前页面的XLOG地址 */
/*
* 当当前页面放不下一条完整记录时,我们会在下一个页面继续放,xlp_rem_len存储了先前页面
* 记录剩余的字节数。注意,xlp_rem_len包含了备份区块的数据,也就是说它会在第一个首部跟
* 踪xl_tot_len而不是xl_len。还要注意,数据不一定是对齐的。
*/
uint32 xlp_rem_len; /* total len of remaining data for record */
} XLogPageHeaderData;

#define SizeOfXLogShortPHD MAXALIGN(sizeof(XLogPageHeaderData))

typedef XLogPageHeaderData *XLogPageHeader;

​ 如果一个页面是一个逻辑段文件的第一个页面,那么在页面头部信息标志位会设置XLP_LONG_HEADER标记,那么将在原页面头部信息的基础上使用一个长的XLOG页面头部XLogLongPageHeaderData,其结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
* 当xlp_info设置了XLP_LONG_HEADER标记位时,我们将在页首部中存储额外的字段。
* (通常是在XLOG文件中的第一个页面中)额外的字段用于确保文件的正确性。
*/
typedef struct XLogLongPageHeaderData
{
XLogPageHeaderData std; /* 标准首部 */
uint64 xlp_sysid; /* 来自pg_control中的系统标识符 */
uint32 xlp_seg_size; /* 交叉校验 */
uint32 xlp_xlog_blcksz;/* 交叉校验 */
} XLogLongPageHeaderData;

#define SizeOfXLogLongPHD MAXALIGN(sizeof(XLogLongPageHeaderData))

typedef XLogLongPageHeaderData *XLogLongPageHeader;

2、xlog日志记录结构信息

​ XLOG Record由两部分组成,第一部分是XLOG Record的头部信息,大小固定(24 Bytes),对应的结构体是XLogRecord;第二部分是XLOG Record data

xlog记录存储布局:

1
2
3
4
5
6
7
8
9
Fixed-size header (XLogRecord struct)
XLogRecordBlockHeader struct
XLogRecordBlockHeader struct

XLogRecordDataHeader[Short|Long] struct
block data
block data

main data

1)XLogRecord

XLOG Record按存储的数据内容来划分,大体可以分为三类:

  • Record for backup block:存储full-write-page的block,这种类型Record是为了解决page部分写的问题。在checkpoint完成后第一次修改数据page,在记 录此变更写入事务日志文件时整页写入(需设置参数full_page_write,默认为打开);

  • Record for tuple data block:存储page中的tuple变更,使用这种类型的Record记录;

  • Record for Checkpoint:在checkpoint发生时,在事务日志文件中记录checkpoint信息。

XLogRecord记录了Xlog记录的相关控制信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//日志之间有链接关系,xl_prev指向上一条日志的起始位置,下一条日志的位置用xl_tot_len可以找到,日志之间形成“双向链表”。
typedef struct XLogRecord
{
//整条XLog记录的长度
uint32 xl_tot_len; /* total len of entire record */
//事务ID
TransactionId xl_xid; /* xact id */
//上一条日志的LSN
XLogRecPtr xl_prev; /* ptr to previous record in log */
//产生这个记录的动作,xl_info低4位保存flag信息,高4位保存日志动作信息。
uint8 xl_info; /* flag bits, see below */
//日志记录对应的资源管理器
RmgrId xl_rmid; /* resource manager for this record */
/* 2 bytes of padding here, initialize to zero */
//本记录的CRC
pg_crc32c xl_crc; /* CRC for this record */

/* XLogRecordBlockHeaders and XLogRecordDataHeader follow, no padding */

} XLogRecord;

其中,xl_info被资源管理器使用,表示该日志是哪种类型的日志,其取值如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* XLOG info values for XLOG rmgr */
#define XLOG_CHECKPOINT_SHUTDOWN 0x00
#define XLOG_CHECKPOINT_ONLINE 0x10
#define XLOG_NOOP 0x20
#define XLOG_NEXTOID 0x30
#define XLOG_SWITCH 0x40
#define XLOG_BACKUP_END 0x50
#define XLOG_PARAMETER_CHANGE 0x60
#define XLOG_RESTORE_POINT 0x70
#define XLOG_FPW_CHANGE 0x80
#define XLOG_END_OF_RECOVERY 0x90
#define XLOG_FPI_FOR_HINT 0xA0
#define XLOG_FPI 0xB0
/* 0xC0 is used in Postgres 9.5-11 */
#define XLOG_OVERWRITE_CONTRECORD 0xD0

2)XLOG Record data

XLOG Record data是存储实际数据的地方,由以下几部分组成:

  • 0…N个XLogRecordBlockHeader,每一个XLogRecordBlockHeader对应一个block data;

  • XLogRecordDataHeader[Short|Long],如数据大小<256 Bytes,则使用Short格式,否则使用Long格式;

  • block data:full-write-page data和tuple data。对于full-write-page data,如启用了压缩,则数据压缩存储,压缩后该page相关的元数据存储在XLogRecordBlockCompressHeader中。block data的个数和XLogRecordBlockHeader对应;

  • main data: checkpoint等日志数据.


(1)BLOCK
①XLogRecordBlockHeader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct XLogRecordBlockHeader
{
//【一个记录中可以有多个block(MAX: 32),此id是block的序号】
uint8 id; /* block reference ID */
//【关系中的分支,以及标记位】
uint8 fork_flags; /* fork within the relation, and flags */
//【决定tupledata中存储的数据的长度(不包括page image)】
uint16 data_length; /* number of payload bytes (not including page
* image) */

/* If BKPBLOCK_HAS_IMAGE, an XLogRecordBlockImageHeader struct follows */
/* If BKPBLOCK_SAME_REL is not set, a RelFileNode follows */
/* BlockNumber follows */
} XLogRecordBlockHeader;

②XLogRecordBlockImageHeader

wal记录是一个full page write记录时,存在此结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef struct XLogRecordBlockImageHeader
{
//【保存的page的总长度(去除空洞数据、且压缩后的长度)】
uint16 length; /* number of page image bytes */
//【空洞前面的字节数】
uint16 hole_offset; /* number of bytes before "hole" */
//【标志位,记录是否包含空洞数据,是否进行了压缩】
uint8 bimg_info; /* flag bits, see below */

/*
* If BKPIMAGE_HAS_HOLE and BKPIMAGE_IS_COMPRESSED, an
* XLogRecordBlockCompressHeader struct follows.
*/
} XLogRecordBlockImageHeader

// bimg_info可能的取值如下:
/* Information stored in bimg_info */
#define BKPIMAGE_HAS_HOLE 0x01 /* page image has "hole" */
#define BKPIMAGE_IS_COMPRESSED 0x02 /* page image is compressed */
③XLogRecordBlockCompressHeader

此结构记录空洞数据的大小

1
2
3
4
5
6
7
8
/*
* Extra header information used when page image has "hole" and
* is compressed.
*/
typedef struct XLogRecordBlockCompressHeader
{
uint16 hole_length; /* number of bytes in "hole" */
} XLogRecordBlockCompressHeader;
④RelFileNode

此结构记录了此block所属的表。如果当前block与前一个block来源于同一个表时,那么fork_flags中就不会有BKPBLOCK_SAME_REL标志位

1
2
3
4
5
6
typedef struct RelFileNode
{
Oid spcNode; /* tablespace */
Oid dbNode; /* database */
Oid relNode; /* relation */
} RelFileNode;
⑤BlockNumber

记录此block记录的page的块号。


(2)XLogRecordDataHeaderLong/XLogRecordDataHeaderShort

此结构被record中的maindata(checkpoint等日志数据)部分使用,当maindata的size小于256时使用XLogRecordDataHeaderShort结构
否则使用XLogRecordDataHeaderLong结构

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct XLogRecordDataHeaderShort
{
uint8 id; /* XLR_BLOCK_ID_DATA_SHORT */
uint8 data_length; /* number of payload bytes */
} XLogRecordDataHeaderShort;


typedef struct XLogRecordDataHeaderLong
{
uint8 id; /* XLR_BLOCK_ID_DATA_LONG */
/* followed by uint32 data_length, unaligned */
} XLogRecordDataHeaderLong;
(3)block data(区块数据与XLogRecordBlockHeader对应)

block data包含full-write-page data(全页写日志记录)和tuple data(更新日志记录)两种类型数据

(4)main data(主数据与XLogRecordDataHeader对应)

main data部分保存非buff性的数据,比如checkpoint等日志数据.

二、xlog记录写入