当前位置:   article > 正文

postgresql源码学习(二十)—— 事务日志①-日志格式_postgresql xlog的格式

postgresql xlog的格式

关于WAL日志的一些基础知识,可以参考之前的文章,本篇侧重于源码部分。

pg 崩溃恢复篇(一)—— WAL的作用与全页写机制_Hehuyi_In的博客-CSDN博客_pg wal
pg 崩溃恢复篇(二)—— WAL文件结构及管理_Hehuyi_In的博客-CSDN博客_wal文件

一、 日志组成结构

来看这个图

层次比较多,具体来看

  • 每个WAL文件(日志段)大小为16M,它在内部划分为多个页面,每个页大小为8K(这也是pg需要全页写的原因)
  • 每个日志页由页头信息(Header)+日志记录(Record)组成
  • Header分为两类:
  • XLogLongPageHeaderData:日志段第一个页的Header信息(每个段只有一个,图中深蓝色部分),存放日志段的长度、段页面大小等信息
  • XLogPageHeaderData:日志段其他页的Header信息(除第一个页外每个日志页都有一个,图中浅蓝色部分),存放事务日志对应的版本、时间线等信息
  • XLogLongPageHeaderData包含XLogPageHeaderData及一些额外信息
  • 每条日志记录又由XLogRecord结构体+数据(XLOG Record data)组成,它是事务日志的最小单元,每个日志记录都表示修改数据库的一个动作
  • 日志数据又可以再分为:块头(XLogRecordBlockHeader)+日志头(XLogRecordDataHeader)+块数据(Block Data)+主数据(Main Data)

二、 日志页头信息

以下按照代码中出现的先后顺序排列

1. 通用页头信息 XLogPageHeaderData

       日志段其他页的Header信息(除第一个页外每个日志页都有一个,图中浅蓝色部分),存放事务日志对应的版本、时间线等信息。

  1. /*
  2. * Each page of XLOG file has a header like this:
  3. */
  4. #define XLOG_PAGE_MAGIC 0xD10D /* can be used as WAL version indicator,事务日志版本信息 */
  5. typedef struct XLogPageHeaderData
  6. {
  7. uint16 xlp_magic; /* magic value for correctness checks,正确性校验位 */
  8. uint16 xlp_info; /* flag bits, see below,标志位,参考下方 */
  9. TimeLineID xlp_tli; /* TimeLineID of first record on page,页面中第一条记录的时间线id */
  10. XLogRecPtr xlp_pageaddr; /* XLOG address of this page,本日志页的首地址 */
  11. /*
  12. * 当页面剩余空间不足以保存整条记录时,需要保存到下一个日志页,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.)
  13. */
  14. uint32 xlp_rem_len; /* total len of remaining data for record,当前页面续接前一页面的日志长度 */
  15. } XLogPageHeaderData;
  16. // XLogPageHeaderData的大小
  17. #define SizeOfXLogShortPHD MAXALIGN(sizeof(XLogPageHeaderData))
  18. // 定义XLogPageHeaderData对应指针
  19. typedef XLogPageHeaderData *XLogPageHeader;

跨页访问类似于

2. 首页头信息 XLogLongPageHeaderData

  • XLogLongPageHeaderData:日志段第一个页的Header信息(每个段只有一个,图中深蓝色部分),存放日志段的长度、段页面大小等信息
  • XLogLongPageHeaderData包含XLogPageHeaderData及一些额外信息
  1. /*
  2. * 当设置了XLP_LONG_HEADER标记位时(只在每个WAL日志段的第一个页会设),我们还要在页头存储一些额外信息,这些额外信息由于精确定位文件
  3. */
  4. typedef struct XLogLongPageHeaderData
  5. {
  6. XLogPageHeaderData std; /* standard header fields,标准页头信息 */
  7. uint64 xlp_sysid; /* system identifier from pg_control,来自控制文件的系统id */
  8. uint32 xlp_seg_size; /* just as a cross-check,日志段大小,用于检查 */
  9. uint32 xlp_xlog_blcksz; /* just as a cross-check,日志页大小,用于检查 */
  10. } XLogLongPageHeaderData;
  11. // XLogLongPageHeaderData的大小
  12. #define SizeOfXLogLongPHD MAXALIGN(sizeof(XLogLongPageHeaderData))
  13. // 定义XLogLongPageHeaderData对应指针
  14. typedef XLogLongPageHeaderData *XLogLongPageHeader;

3. 一些宏定义

  1. /* When record crosses page boundary, set this flag in new page's header,当日志记录跨页时,设置该标记 */
  2. #define XLP_FIRST_IS_CONTRECORD 0x0001
  3. /* This flag indicates a "long" page header,是long header信息(即XLogLongPageHeaderData) */
  4. #define XLP_LONG_HEADER 0x0002
  5. /* This flag indicates backup blocks starting in this page are optional,在pg_start_backup函数开始后,数据库会进入FPW状态,当备份停止时,在WAL日志上打上XLP_BKP_REMOVABLE标记。从这里开始FPW不是必须执行,进入可选状态 */
  6. #define XLP_BKP_REMOVABLE 0x0004
  7. /* All defined flag bits in xlp_info (used for validity checking of header),前面提到的flag标记位,用于检查header有效性 */
  8. #define XLP_ALL_FLAGS 0x0007
  9. //判断页类型,看是用long页大小还是标准页大小
  10. #define XLogPageHeaderSize(hdr) \
  11. (((hdr)->xlp_info & XLP_LONG_HEADER) ? SizeOfXLogLongPHD : SizeOfXLogShortPHD)
  12. /* wal_segment_size can range from 1MB to 1GB,日志段最小及最大大小 */
  13. #define WalSegMinSize 1024 * 1024
  14. #define WalSegMaxSize 1024 * 1024 * 1024

再来看日志记录部分

内容也比较多,我们按图中的层次分别介绍:

  • 日志记录通用头XLogRecord
  • 日志记录头信息:日志记录块头XLogRecordBlockHeader+日志记录数据头XLogRecordDataHeader
  • 日志记录数据:块数据Block Data+主数据Main Data。

三、 日志记录通用头 XLogRecord

  1. typedef struct XLogRecord
  2. {
  3. uint32 xl_tot_len; /* total len of entire record,记录总长度 */
  4. TransactionId xl_xid; /* xact id,事务id */
  5. XLogRecPtr xl_prev; /* ptr to previous record in log,指向日志中前一条记录的指针 */
  6. uint8 xl_info; /* flag bits, see below,记录标记位和产生这个记录的动作,参考下方 */
  7. RmgrId xl_rmid; /* resource manager for this record,该记录的资源管理器信息 */
  8. /* 2 bytes of padding here, initialize to zero */
  9. pg_crc32c xl_crc; /* CRC for this record,该记录的CRC(循环冗余校验) */
  10. /* XLogRecordBlockHeaders and XLogRecordDataHeader follow, no padding,后面是另两个Header结构体 */
  11. } XLogRecord;

xl_info记录标记位和产生这个记录的动作:

  • 其中低4位存储两种标记信息:XLR_SPECIAL_REL_UPDATE和XLR_CHECK_CONSISTENCY,由XLogInsert函数的调用者传入
  1. /*
  2. * 如果WAL记录使用特殊的方式(不涉及通常块引用)更新了关系的存储文件,设置此标记。PostgreSQL本身并不使用这种方法,但它允许外部工具读取WAL并跟踪修改后的块,以识别这种特殊的记录类型。
  3. */
  4. #define XLR_SPECIAL_REL_UPDATE 0x01
  5. /*
  6. * 在恢复时强制执行一致性检查。如过启用,会执行全页写操作,并在恢复时用它进行一致性检查。在需要时,XLogInsert的调用者可设置此标记,但如果rmgr启用了wal_consistency_checking,则会无条件执行一致性检查。
  7. */
  8. #define XLR_CHECK_CONSISTENCY 0x02
  • 高4位表示产生这个记录的动作(最多16种),不同的resource id下动作信息不同,因此每种resource id对应的动作数量会受限制。以Heap操作为例,其resource id是RM_HEAP_ID
  1. /*
  2. * XLOG allows to store some information in high 4 bits of log
  3. * record xl_info field. We use 3 for opcode and one for init bit.
  4. */
  5. #define XLOG_HEAP_INSERT 0x00
  6. #define XLOG_HEAP_DELETE 0x10
  7. #define XLOG_HEAP_UPDATE 0x20
  8. #define XLOG_HEAP_TRUNCATE 0x30
  9. #define XLOG_HEAP_HOT_UPDATE 0x40
  10. #define XLOG_HEAP_CONFIRM 0x50
  11. #define XLOG_HEAP_LOCK 0x60
  12. #define XLOG_HEAP_INPLACE 0x70
  13. #define XLOG_HEAP_OPMASK 0x70
  14. /*
  15. * When we insert 1st item on new page in INSERT, UPDATE, HOT_UPDATE,
  16. * or MULTI_INSERT, we can (and we do) restore entire page in redo。在日志页写入第一条信息时进行标记,用于全页写
  17. */
  18. #define XLOG_HEAP_INIT_PAGE 0x80

四、 日志记录块头

1. XLogRecordBlockHeader

  1. /*
  2. * Header info for block data appended to an XLOG record.日志记录中块数据的头信息
  3. */
  4. typedef struct XLogRecordBlockHeader
  5. {
  6. uint8 id; /* block reference ID,块引用id */
  7. uint8 fork_flags; /* fork within the relation, and flags,表中的分支以及标记位 */
  8. uint16 data_length; /* number of payload bytes (not including page image),荷载字节数,不包括页面镜像和XLogRecordBlockHeader结构体本身 */
  9. /* If BKPBLOCK_HAS_IMAGE, an XLogRecordBlockImageHeader struct follows,如果设置了BKPBLOCK_HAS_IMAGE,还会包含XLogRecordBlockImageHeader结构体 */
  10. /* If BKPBLOCK_SAME_REL is not set, a RelFileNode follows,如果没有设置BKPBLOCK_SAME_REL,则会包含RelFileNode */
  11. /* BlockNumber follows,后续为块号 */
  12. } XLogRecordBlockHeader;
  13. #define SizeOfXLogRecordBlockHeader (offsetof(XLogRecordBlockHeader, data_length) + sizeof(uint16))

BlockNumber的定义在block.h文件,是一个32位无符号整数,可用值为 00xFFFFFFFE。

  1. typedef uint32 BlockNumber;
  2. #define InvalidBlockNumber ((BlockNumber) 0xFFFFFFFF)
  3. #define MaxBlockNumber ((BlockNumber) 0xFFFFFFFE)

从图中可以看到,XLogRecordBlockHeader可能包括几个可选项:

  • XLogRecordBlockImageHeader:包含full page image(全页镜像,也叫备份区块,用于全页写),后面会提到
  • XLogRecordBlockCompressHeader:启用压缩
  • RelFileNoderelfilenode.h):如果没有设置BKPBLOCK_SAME_REL

2. XLogRecordBlockImageHeader

当包含full-page image(备份区块,即设置了BKPBLOCK_HAS_IMAGE)时,附加的头信息。

  1. /*
  2. * Additional header information when a full-page image is included
  3. * (i.e. when BKPBLOCK_HAS_IMAGE is set). 当包含full-page image(备份区块,即设置了BKPBLOCK_HAS_IMAGE)时,附加的头信息
  4. *
  5. * XLOG代码知道PG数据页通常在中间包含一些未使用的 hole(孔、洞,即空闲空间),大小为零字节。既然我们知道hole都是零,因此可以从存储的数据中删除它(而且它也没有被计入XLOG记录的CRC中)。 因此,实际的块数据量为 BLCKSZ - hole的大小。
  6. *
  7. * 另外,在启用wal_compression时,会在去掉hole后,尝试使用PGLZ压缩算法压缩full page image。这可以减小WAL容量,但会增加额外的CPU消耗。
  8. * 在这种情况下,由于hole的长度不能通过从BLCKSZ中减去page image字节数来计算,所以它基本上需要作为额外的信息来存储。但如果hole不存在,我们可以假设hole的大小为0,不需要存储额外的信息。
  9. * 请注意,如果压缩节省的字节数小于额外信息的长度,那么在WAL中存储page image的原始版本,而不是压缩后的版本。
  10. * 因此,当page image被成功压缩时,实际的块数据量小于BLCKSZ-hole的大小-额外信息的大小。
  11. */
  12. typedef struct XLogRecordBlockImageHeader
  13. {
  14. uint16 length; /* number of page image bytes,页面镜像字节数 */
  15. uint16 hole_offset; /* number of bytes before "hole",hole前面的字节数 */
  16. uint8 bimg_info; /* flag bits, see below,标记位 */
  17. /*
  18. * If BKPIMAGE_HAS_HOLE and BKPIMAGE_IS_COMPRESSED, an
  19. * XLogRecordBlockCompressHeader struct follows.
  20. */
  21. } XLogRecordBlockImageHeader;
  22. /* Information stored in bimg_info */
  23. #define BKPIMAGE_HAS_HOLE 0x01 /* page image has "hole" */
  24. #define BKPIMAGE_IS_COMPRESSED 0x02 /* page image is compressed */
  25. #define BKPIMAGE_APPLY 0x04 /* page image should be restored during replay */

3. XLogRecordBlockCompressHeader

  1. /*
  2. * Extra header information used when page image has "hole" and
  3. * is compressed.
  4. */
  5. typedef struct XLogRecordBlockCompressHeader
  6. {
  7. uint16 hole_length; /* number of bytes in "hole" */
  8. } XLogRecordBlockCompressHeader;
  9. #define SizeOfXLogRecordBlockCompressHeader \
  10. sizeof(XLogRecordBlockCompressHeader)

4. RelFileNode

这个结构体很简单

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

5. MaxSizeOfXLogRecordBlockHeader

XLogRecordBlockHeader最大size,最大就是每部分都有,然后加起来。

  1. /*
  2. * Maximum size of the header for a block reference. This is used to size a
  3. * temporary buffer for constructing the header.
  4. */
  5. #define MaxSizeOfXLogRecordBlockHeader \
  6. (SizeOfXLogRecordBlockHeader + \
  7. SizeOfXLogRecordBlockImageHeader + \
  8. SizeOfXLogRecordBlockCompressHeader + \
  9. sizeof(RelFileNode) + \
  10. sizeof(BlockNumber))

五、 日志记录数据头 XLogRecordDataHeaderShort/Long

       main data部分的头信息,分为长短两种。如果数据长度小于256 bytes则用短的,并用一个字节保存长度,否则用长的。

  1. /*
  2. * These structs are currently not used in the code, they are here just for
  3. * documentation purposes. 这些结构体现在已经没有在代码中使用了,还保留在这里只是为了文档记录的目的。
  4. */
  5. typedef struct XLogRecordDataHeaderShort
  6. {
  7. uint8 id; /* XLR_BLOCK_ID_DATA_SHORT */
  8. uint8 data_length; /* number of payload bytes */
  9. } XLogRecordDataHeaderShort;
  10. #define SizeOfXLogRecordDataHeaderShort (sizeof(uint8) * 2)
  1. typedef struct XLogRecordDataHeaderLong
  2. {
  3. uint8 id; /* XLR_BLOCK_ID_DATA_LONG */
  4. /* followed by uint32 data_length, unaligned */
  5. } XLogRecordDataHeaderLong;
  6. #define SizeOfXLogRecordDataHeaderLong (sizeof(uint8) + sizeof(uint32))

六、 日志记录真正的数据部分

        这里我们合并介绍块数据 Block Data和主数据 Main Data,因为它们是相关的。

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

  • Record for backup block(备份区块):存储full-write-page的block,是为了解决日志页部分写的问题;
  • Record for tuple data block(非备份区块):在full-write-page后,记录相应的page中的tuple变更
  • Record for Checkpointcheckpoint发生时,在事务日志文件中记录checkpoint信息(其中包括Redo point).

每种类型具体包含的头和数据信息都不完全相同,可以结合前面结构体介绍去看。

https://img-blog.csdnimg.cn/20190710154417292.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9kYXp1aWJhMDA4LmJsb2cuY3Nkbi5uZXQ=,size_16,color_FFFFFF,t_70

      在之前的文章 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 日志构建原理 | 学习笔记

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/806150
推荐阅读
相关标签
  

闽ICP备14008679号