赞
踩
关于WAL日志的一些基础知识,可以参考之前的文章,本篇侧重于源码部分。
pg 崩溃恢复篇(一)—— WAL的作用与全页写机制_Hehuyi_In的博客-CSDN博客_pg wal
pg 崩溃恢复篇(二)—— WAL文件结构及管理_Hehuyi_In的博客-CSDN博客_wal文件
来看这个图
层次比较多,具体来看
- XLogLongPageHeaderData:日志段第一个页的Header信息(每个段只有一个,图中深蓝色部分),存放日志段的长度、段页面大小等信息
- XLogPageHeaderData:日志段其他页的Header信息(除第一个页外每个日志页都有一个,图中浅蓝色部分),存放事务日志对应的版本、时间线等信息
- XLogLongPageHeaderData包含XLogPageHeaderData及一些额外信息
以下按照代码中出现的先后顺序排列
日志段其他页的Header信息(除第一个页外每个日志页都有一个,图中浅蓝色部分),存放事务日志对应的版本、时间线等信息。
- /*
- * Each page of XLOG file has a header like this:
- */
-
- #define XLOG_PAGE_MAGIC 0xD10D /* 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,页面中第一条记录的时间线id */
- XLogRecPtr xlp_pageaddr; /* XLOG address of this page,本日志页的首地址 */
-
- /*
- * 当页面剩余空间不足以保存整条记录时,需要保存到下一个日志页,xlp_rem_len就用来记录剩余需要保存的记录长度,它跟踪初始页头的xl_tot_len长度(xlp_rem_len is the number of bytes remaining from a previous page; it tracks xl_tot_len in the initial header.)
- */
- uint32 xlp_rem_len; /* total len of remaining data for record,当前页面续接前一页面的日志长度 */
- } XLogPageHeaderData;
-
- // XLogPageHeaderData的大小
- #define SizeOfXLogShortPHD MAXALIGN(sizeof(XLogPageHeaderData))
- // 定义XLogPageHeaderData对应指针
- typedef XLogPageHeaderData *XLogPageHeader;
跨页访问类似于
- /*
- * 当设置了XLP_LONG_HEADER标记位时(只在每个WAL日志段的第一个页会设),我们还要在页头存储一些额外信息,这些额外信息由于精确定位文件
- */
- typedef struct XLogLongPageHeaderData
- {
- XLogPageHeaderData std; /* standard header fields,标准页头信息 */
- uint64 xlp_sysid; /* system identifier from pg_control,来自控制文件的系统id */
- uint32 xlp_seg_size; /* just as a cross-check,日志段大小,用于检查 */
- uint32 xlp_xlog_blcksz; /* just as a cross-check,日志页大小,用于检查 */
- } XLogLongPageHeaderData;
-
- // XLogLongPageHeaderData的大小
-
- #define SizeOfXLogLongPHD MAXALIGN(sizeof(XLogLongPageHeaderData))
-
- // 定义XLogLongPageHeaderData对应指针
- typedef XLogLongPageHeaderData *XLogLongPageHeader;
- /* When record crosses page boundary, set this flag in new page's header,当日志记录跨页时,设置该标记 */
- #define XLP_FIRST_IS_CONTRECORD 0x0001
-
- /* This flag indicates a "long" page header,是long header信息(即XLogLongPageHeaderData) */
- #define XLP_LONG_HEADER 0x0002
-
- /* This flag indicates backup blocks starting in this page are optional,在pg_start_backup函数开始后,数据库会进入FPW状态,当备份停止时,在WAL日志上打上XLP_BKP_REMOVABLE标记。从这里开始FPW不是必须执行,进入可选状态 */
- #define XLP_BKP_REMOVABLE 0x0004
-
- /* All defined flag bits in xlp_info (used for validity checking of header),前面提到的flag标记位,用于检查header有效性 */
- #define XLP_ALL_FLAGS 0x0007
-
- //判断页类型,看是用long页大小还是标准页大小
- #define XLogPageHeaderSize(hdr) \
- (((hdr)->xlp_info & XLP_LONG_HEADER) ? SizeOfXLogLongPHD : SizeOfXLogShortPHD)
-
- /* wal_segment_size can range from 1MB to 1GB,日志段最小及最大大小 */
- #define WalSegMinSize 1024 * 1024
- #define WalSegMaxSize 1024 * 1024 * 1024
再来看日志记录部分
内容也比较多,我们按图中的层次分别介绍:
- typedef struct XLogRecord
- {
- uint32 xl_tot_len; /* total len of entire record,记录总长度 */
- TransactionId xl_xid; /* xact id,事务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,该记录的CRC(循环冗余校验) */
-
- /* XLogRecordBlockHeaders and XLogRecordDataHeader follow, no padding,后面是另两个Header结构体 */
-
- } XLogRecord;
xl_info记录标记位和产生这个记录的动作:
- /*
- * 如果WAL记录使用特殊的方式(不涉及通常块引用)更新了关系的存储文件,设置此标记。PostgreSQL本身并不使用这种方法,但它允许外部工具读取WAL并跟踪修改后的块,以识别这种特殊的记录类型。
- */
- #define XLR_SPECIAL_REL_UPDATE 0x01
-
- /*
- * 在恢复时强制执行一致性检查。如过启用,会执行全页写操作,并在恢复时用它进行一致性检查。在需要时,XLogInsert的调用者可设置此标记,但如果rmgr启用了wal_consistency_checking,则会无条件执行一致性检查。
- */
- #define XLR_CHECK_CONSISTENCY 0x02
- /*
- * XLOG allows to store some information in high 4 bits of log
- * record xl_info field. We use 3 for opcode and one for init bit.
- */
- #define XLOG_HEAP_INSERT 0x00
- #define XLOG_HEAP_DELETE 0x10
- #define XLOG_HEAP_UPDATE 0x20
- #define XLOG_HEAP_TRUNCATE 0x30
- #define XLOG_HEAP_HOT_UPDATE 0x40
- #define XLOG_HEAP_CONFIRM 0x50
- #define XLOG_HEAP_LOCK 0x60
- #define XLOG_HEAP_INPLACE 0x70
- #define XLOG_HEAP_OPMASK 0x70
- /*
- * When we insert 1st item on new page in INSERT, UPDATE, HOT_UPDATE,
- * or MULTI_INSERT, we can (and we do) restore entire page in redo。在日志页写入第一条信息时进行标记,用于全页写
- */
- #define XLOG_HEAP_INIT_PAGE 0x80
- /*
- * Header info for block data appended to an XLOG record.日志记录中块数据的头信息
- */
- typedef struct XLogRecordBlockHeader
- {
- uint8 id; /* block reference ID,块引用id */
- uint8 fork_flags; /* fork within the relation, and flags,表中的分支以及标记位 */
- uint16 data_length; /* number of payload bytes (not including page image),荷载字节数,不包括页面镜像和XLogRecordBlockHeader结构体本身 */
-
- /* If BKPBLOCK_HAS_IMAGE, an XLogRecordBlockImageHeader struct follows,如果设置了BKPBLOCK_HAS_IMAGE,还会包含XLogRecordBlockImageHeader结构体 */
- /* If BKPBLOCK_SAME_REL is not set, a RelFileNode follows,如果没有设置BKPBLOCK_SAME_REL,则会包含RelFileNode */
- /* BlockNumber follows,后续为块号 */
- } XLogRecordBlockHeader;
-
- #define SizeOfXLogRecordBlockHeader (offsetof(XLogRecordBlockHeader, data_length) + sizeof(uint16))
BlockNumber的定义在block.h文件,是一个32位无符号整数,可用值为 0到0xFFFFFFFE。
- typedef uint32 BlockNumber;
- #define InvalidBlockNumber ((BlockNumber) 0xFFFFFFFF)
- #define MaxBlockNumber ((BlockNumber) 0xFFFFFFFE)
从图中可以看到,XLogRecordBlockHeader可能包括几个可选项:
当包含full-page image(备份区块,即设置了BKPBLOCK_HAS_IMAGE)时,附加的头信息。
- /*
- * Additional header information when a full-page image is included
- * (i.e. when BKPBLOCK_HAS_IMAGE is set). 当包含full-page image(备份区块,即设置了BKPBLOCK_HAS_IMAGE)时,附加的头信息
- *
- * XLOG代码知道PG数据页通常在中间包含一些未使用的 hole(孔、洞,即空闲空间),大小为零字节。既然我们知道hole都是零,因此可以从存储的数据中删除它(而且它也没有被计入XLOG记录的CRC中)。 因此,实际的块数据量为 BLCKSZ - hole的大小。
- *
- * 另外,在启用wal_compression时,会在去掉hole后,尝试使用PGLZ压缩算法压缩full page image。这可以减小WAL容量,但会增加额外的CPU消耗。
- * 在这种情况下,由于hole的长度不能通过从BLCKSZ中减去page image字节数来计算,所以它基本上需要作为额外的信息来存储。但如果hole不存在,我们可以假设hole的大小为0,不需要存储额外的信息。
- * 请注意,如果压缩节省的字节数小于额外信息的长度,那么在WAL中存储page image的原始版本,而不是压缩后的版本。
- * 因此,当page image被成功压缩时,实际的块数据量小于BLCKSZ-hole的大小-额外信息的大小。
- */
-
- typedef struct XLogRecordBlockImageHeader
- {
- uint16 length; /* number of page image bytes,页面镜像字节数 */
- uint16 hole_offset; /* number of bytes before "hole",hole前面的字节数 */
- uint8 bimg_info; /* flag bits, see below,标记位 */
-
- /*
- * If BKPIMAGE_HAS_HOLE and BKPIMAGE_IS_COMPRESSED, an
- * XLogRecordBlockCompressHeader struct follows.
- */
- } XLogRecordBlockImageHeader;
-
- /* Information stored in bimg_info */
- #define BKPIMAGE_HAS_HOLE 0x01 /* page image has "hole" */
- #define BKPIMAGE_IS_COMPRESSED 0x02 /* page image is compressed */
- #define BKPIMAGE_APPLY 0x04 /* page image should be restored during replay */
- /*
- * Extra header information used when page image has "hole" and
- * is compressed.
- */
- typedef struct XLogRecordBlockCompressHeader
- {
- uint16 hole_length; /* number of bytes in "hole" */
- } XLogRecordBlockCompressHeader;
-
- #define SizeOfXLogRecordBlockCompressHeader \
- sizeof(XLogRecordBlockCompressHeader)
这个结构体很简单
- typedef struct RelFileNode
- {
- Oid spcNode; /* tablespace */
- Oid dbNode; /* database */
- Oid relNode; /* relation */
- } RelFileNode;
XLogRecordBlockHeader最大size,最大就是每部分都有,然后加起来。
- /*
- * Maximum size of the header for a block reference. This is used to size a
- * temporary buffer for constructing the header.
- */
- #define MaxSizeOfXLogRecordBlockHeader \
- (SizeOfXLogRecordBlockHeader + \
- SizeOfXLogRecordBlockImageHeader + \
- SizeOfXLogRecordBlockCompressHeader + \
- sizeof(RelFileNode) + \
- sizeof(BlockNumber))
main data部分的头信息,分为长短两种。如果数据长度小于256 bytes则用短的,并用一个字节保存长度,否则用长的。
- /*
- * These structs are currently not used in the code, they are here just for
- * documentation purposes. 这些结构体现在已经没有在代码中使用了,还保留在这里只是为了文档记录的目的。
- */
- typedef struct XLogRecordDataHeaderShort
- {
- uint8 id; /* XLR_BLOCK_ID_DATA_SHORT */
- uint8 data_length; /* number of payload bytes */
- } XLogRecordDataHeaderShort;
-
- #define SizeOfXLogRecordDataHeaderShort (sizeof(uint8) * 2)
- typedef struct XLogRecordDataHeaderLong
- {
- uint8 id; /* XLR_BLOCK_ID_DATA_LONG */
- /* followed by uint32 data_length, unaligned */
- } XLogRecordDataHeaderLong;
-
- #define SizeOfXLogRecordDataHeaderLong (sizeof(uint8) + sizeof(uint32))
这里我们合并介绍块数据 Block Data和主数据 Main Data,因为它们是相关的。
XLOG Record按存储的数据内容来划分,大体可以分为三类:
每种类型具体包含的头和数据信息都不完全相同,可以结合前面结构体介绍去看。
在之前的文章 pg 崩溃恢复篇(三)—— 走近XLOG记录_Hehuyi_In的博客-CSDN博客 也有记录过,可以参考。
参考
《PostgreSQL技术内幕:事务处理深度探索》第4章
PostgreSQL DBA(17) - XLOG Record data内部结构 - 简书
PostgreSQL 源码解读(109)- WAL#5(相关数据结构) - 简书
PostgreSQL xlog格式 backup full page_yzs87的博客-CSDN博客
PostgreSQL xlog格式之checkpoint_yzs87的博客-CSDN博客
PostgreSQL xlog格式之no backup full page_ITPUB博客
Postgresql Wal 日志构建原理 | 学习笔记
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。