IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> postgresql源码学习(二十)—— 故障恢复①-事务日志格式 -> 正文阅读

[大数据]postgresql源码学习(二十)—— 故障恢复①-事务日志格式

关于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信息(除第一个页外每个日志页都有一个,图中浅蓝色部分),存放事务日志对应的版本、时间线等信息。

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

跨页访问类似于

2. 首页头信息 XLogLongPageHeaderData

  • XLogLongPageHeaderData:日志段第一个页的Header信息(每个段只有一个,图中深蓝色部分),存放日志段的长度、段页面大小等信息
  • XLogLongPageHeaderData包含XLogPageHeaderData及一些额外信息
/*
 * 当设置了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;

3. 一些宏定义

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

再来看日志记录部分

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

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

?

三、 日志记录通用头 XLogRecord

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记录标记位和产生这个记录的动作:

  • 其中低4位存储两种标记信息:XLR_SPECIAL_REL_UPDATE和XLR_CHECK_CONSISTENCY,由XLogInsert函数的调用者传入
/*
 * 如果WAL记录使用特殊的方式(不涉及通常块引用)更新了关系的存储文件,设置此标记。PostgreSQL本身并不使用这种方法,但它允许外部工具读取WAL并跟踪修改后的块,以识别这种特殊的记录类型。 
*/
#define XLR_SPECIAL_REL_UPDATE	0x01

/*
 * 在恢复时强制执行一致性检查。如过启用,会执行全页写操作,并在恢复时用它进行一致性检查。在需要时,XLogInsert的调用者可设置此标记,但如果rmgr启用了wal_consistency_checking,则会无条件执行一致性检查。 
*/
#define XLR_CHECK_CONSISTENCY	0x02
  • 高4位表示产生这个记录的动作(最多16种),不同的resource id下动作信息不同,因此每种resource id对应的动作数量会受限制。以Heap操作为例,其resource id是RM_HEAP_ID
/*
* 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

四、 日志记录块头

1. XLogRecordBlockHeader

/*
 * 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位无符号整数,可用值为 00xFFFFFFFE。

typedef uint32 BlockNumber;
#define InvalidBlockNumber		((BlockNumber) 0xFFFFFFFF)
#define MaxBlockNumber			((BlockNumber) 0xFFFFFFFE)

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

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

2. XLogRecordBlockImageHeader

当包含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 */

3. XLogRecordBlockCompressHeader

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

4. RelFileNode

这个结构体很简单

typedef struct RelFileNode
{
	Oid			spcNode;		/* tablespace */
	Oid			dbNode;			/* database */
	Oid			relNode;		/* relation */
} RelFileNode;

5. MaxSizeOfXLogRecordBlockHeader

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))

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

? ? ? ?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按存储的数据内容来划分,大体可以分为三类:

  • 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博客

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-06-06 17:25:11  更:2022-06-06 17:25:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 0:01:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码