PostgreSQL 9.6源码解析之XLOG生成

一、WAL日志概述

pg中wal子系统的存在是为了故障恢复,它也被用于基于时间点的恢复、基于日志搬迁的Hot-standby复制。以下想描述一点wal日志的设计理念。
wal日志的一个基本假设是日志条目必须先于它所描述的数据变化页面持久化到稳定存储(如硬盘)。这确保重放日志到它的末端将使数据库可以重新达到一致性状态(不存在部分执行的事务)。为了达到这一点,每一个数据页面(堆或索引页面)被标记上了影响本页面的最新xlog记录的LSN号。
LSN号,全称Log sequence number, 实际上代表wal文件位置。
在wal重放时,可以核对页面的LSN来确定本条xlog记录是否已经被重放(如果页面LSN>日志条目的位置,则该条日志已被重放)。

二、事务日志与WAL段文件

xlog详细记录了服务进程对数据库的操作过程。xlog日志文件在内存中按页存放,每个页面大小为8kb,每个页面都有一个头部,头部信息之后才是xlog日志记录。

每个xlog文件都有一个ID,但事实上它被分成一个个固定大小(默认16MB,initdb时可由–wal-segsize指定)的XLOG段文件来存放。xlog文件号和段文件号可以用来唯一确定这个段文件。确定日志文件内一个日志记录的地址时,只需用一个XLOG文件号和日志记录在该文件内的偏移量即可。

三、xlog文件初始化

initdb初始化data时,在函数bootstrap_template1中初始化template1模板库时,通过popen调用postgres程序中的AuxiliaryProcessMain,AuxiliaryProcessMain中调用BootStrapXLOG函数完成XLOG段文件初始化工作。
PostgreSQL 9.6源码解析之XLOG生成_第1张图片

四、xlog文件名

initdb生成xlog文件时,用以下宏生成文件名,

#define XLogFilePath(path, tli, logSegNo)	\
	snprintf(path, MAXPGPATH, XLOGDIR "/%08X%08X%08X", tli,				\
			 (uint32) ((logSegNo) / XLogSegmentsPerXLogId),				\
			 (uint32) ((logSegNo) % XLogSegmentsPerXLogId))
#define XLOG_SEG_SIZE (16 * 1024 * 1024)
#define XLogSegmentsPerXLogId	(UINT64CONST(0x100000000) / XLOG_SEG_SIZE)


note: XLogSegmentsPerXlogId值为256,XLOG_SEG_SIZE为段大小16MB
宏中变量:
path: 代表文件名
tli:代表时间线,初始时间线为1
logSegNo:代表段号,初始值为1

以上值代入宏,经snprint格式化后,path值为:
pg_xlog路径+时间线+(uint32)段号/256+段号%256 --%08X代表按8位16进制数显示
结果即:
pg_xlog/+00000001+00000000+00000001
Note: 此处,可以看出段文件名最后两位最大值为256,转为16进制则段文件名的最后8位最大为000000FF.

五、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,其结构如下:

/*
 * Each page of XLOG file has a header like this:
 */
#define XLOG_PAGE_MAGIC 0xD093	/* can be used as WAL version indicator */

typedef struct XLogPageHeaderData
{
	uint16		xlp_magic;		/* magic value for correctness checks */
	uint16		xlp_info;		/* flag bits, see below */
	TimeLineID	xlp_tli;		/* TimeLineID of first record on page */
	XLogRecPtr	xlp_pageaddr;	/* XLOG address of this page */

	/*
	 * 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 xl_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;

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

/*
 * 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;

2)xlog日志记录结构信息
XLOG Record由两部分组成,第一部分是XLOG Record的头部信息,大小固定(24 Bytes),对应的结构体是XLogRecord;第二部分是XLOG Record data。
xlog记录存储格局:
Fixed-size header (XLogRecord struct)
XLogRecordBlockHeader struct
XLogRecordBlockHeader struct

XLogRecordDataHeader[Short|Long] struct
block data
block data

main data

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记录的相关控制信息,

typedef struct XLogRecord
{
	uint32		xl_tot_len;		/* total len of entire record */
	TransactionId xl_xid;		/* xact id */
	XLogRecPtr	xl_prev;		/* ptr to previous record in log */
	uint8		xl_info;		/* flag bits, see below */
	RmgrId		xl_rmid;		/* resource manager for this record */
	/* 2 bytes of padding here, initialize to zero */
	pg_crc32c	xl_crc;			/* CRC for this record */

	/* XLogRecordBlockHeaders and XLogRecordDataHeader follow, no padding */

} XLogRecord;

xl_tot_len //整条记录总长度
xl_xid //事务ID
xl_prev //在日志中的前一条记录
xl_info //信息标志位
xl_rmid //资源管理器ID

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

/* 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

其中,xl_rmid资源管理器的取值如下:

/* symbol name, textual name, redo, desc, identify, startup, cleanup */
PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, xlog_identify, NULL, NULL)
PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, xact_identify, NULL, NULL)
PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, smgr_identify, NULL, NULL)
PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, clog_identify, NULL, NULL)
PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, dbase_identify, NULL, NULL)
PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, tblspc_identify, NULL, NULL)
PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, multixact_identify, NULL, NULL)
PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, relmap_identify, NULL, NULL)
PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, standby_identify, NULL, NULL)
PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, heap2_identify, NULL, NULL)
PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, heap_identify, NULL, NULL)
PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_identify, NULL, NULL)
PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, hash_identify, NULL, NULL)
PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_identify, gin_xlog_startup, gin_xlog_cleanup)
PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_identify, gist_xlog_startup, gist_xlog_cleanup)
PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, seq_identify, NULL, NULL)
PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_identify, spg_xlog_startup, spg_xlog_cleanup)
PG_RMGR(RM_BRIN_ID, "BRIN", brin_redo, brin_desc, brin_identify, NULL, NULL)
PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_identify, NULL, NULL)
PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL)
PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL)
PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL)

下列出其中较重要几种资源管理器ID含义:
RM_XLOG_ID:该条日志记录的是一个检查点信息
RM_XACT_ID:该条日志记录的是一个事务的提交或终止信息
RM_CLOG_ID:该条日志记录的是CLOG中某一页的初始化
RM_HEAP_ID:该条日志记录的是对堆中元组进行修改的信息

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中;

main data: checkpoint等日志数据.

。。。
细节内容有点多,本篇先写到这里

你可能感兴趣的:(pg源码分析,xlog)