pg 崩溃恢复篇(三)—— 走近XLOG记录

一、 XLOG记录的内部结构

XLOG记录由通用头部分XLogRecord+数据部分组成

在这里插入图片描述

而数据部分又有自己的头部分和数据部分

在这里插入图片描述

1. XLOG记录通用头部

所有XLOG记录都有一个的通用头部分,由结构体XLogRecord定义。9.5及之后定义如下

typedef struct XLogRecord
{
	uint32		xl_tot_len;		/* 记录总长度 */
	TransactionId xl_xid;		/* txid */
	XLogRecPtr	xl_prev;		/* 指向日志中前一条记录的指针 */
	uint8		xl_info;		/* 标记位 */
	RmgrId		xl_rmid;		/* 本记录的资源管理器 */
	/* 这里有2 Bytes的填充,初始化为0 */
	pg_crc32c	xl_crc;			/* 本记录的CDC */
	/* 随后是XLogRecordBlockHeaders与XLogRecordDataHeader,不带填充 */
} XLogRecord;

2. XLOG记录的数据部分(9.5及之后)

在9.5之前,XLOG记录没有通用的格式,因此每种资源管理器都需要定义各自的格式。在这种情况下,维护源代码和实现与WAL相关的新功能变得越来越困难。为了解决这个问题,9.5版本中引入了一种不依赖于资源管理器的通用的结构化格式。

XLOG记录的数据部分可以分为两部分 —— 头部分与数据部分


在这里插入图片描述

头部分包含零或多个XLogRecordBlockHeaders,以及零或一个XLogRecordDataHeaderShort(或XLogRecordDataHeaderLong),但必须至少包含其中一个。

当记录存储整页(即备份块)时,XLogRecordBlockHeader包括XLogRecordBlockImageHeader;如果启用压缩,还会包括XLogRecordBlockCompressHeader。

XLogRecordBlockHeaders定义如下:
在这里插入图片描述
 

XLogRecordDataHeaderShort/Long 定义如下:

如果数据长度小于256B,则使用短版本格式,即使用单个字节来保存长度数值,否则使用长版本格式
在这里插入图片描述
 

XLogRecordBlockImageHeader定义如下:
在这里插入图片描述

XLogRecordBlockCompressHeader定义如下:
在这里插入图片描述
 

数据部分由零个或多个块数据和零个或一个主数据组成,它们分别对应于XLogRecordBlockHeader和XLogRecordDataHeader。

WAL压缩
在9.5或更高版本中,通过设置参数wal_compression = enable,可以使用LZ压缩方法压缩XLOG记录中的整个页。在这种情况下,将添加结构体XLogRecordBlockCompressHeader。
该特征有两个优点和一个缺点。优点是降低了写入记录的I / O成本并抑制了WAL段文件的消耗。缺点是需要消耗大量CPU资源才能进行压缩。

二、 备份块与非备份块

Fig. 9.10. Examples of XLOG records  (version 9.5 or later).

1. 备份块

备份块如上图a所示,它由四个数据结构体和一个数据对象组成:

  • 结构体XLogRecord(通用头部分)
  • 数据部分的头数据XLogRecordBlockHeader,且包含一个LogRecordBlockImageHeader
  • 数据部分的头数据XLogRecordDataHeaderShort
  • 备份块(块数据)
  • 结构体 xl_heap_insert(主数据)

XLogRecordBlockHeader包含用于在数据库集群中定位区块的变量(relfilenode,fork编号和区块号)

XLogRecordImageHeader包含此块的长度和偏移号

XLogRecordDataHeaderShort存储xl_heap_insert结构的长度,该结构是记录的主要数据。

除了在某些特殊情况下(如逻辑解码和推测性插入),一般不使用包含整页镜像的XLOG记录的主数据。它们会在记录重放时被忽略,属于冗余数据,未来可能会对其进行改进。此外,备份块记录的主数据取决于创建它们的语句。例如,UPDATE语句会追加写入xl_heap_lock或xl_heap_updated。

2. 非备份块

备份块如上图b所示,它由四个数据结构和一个数据对象组成:

  • 结构体XLogRecord(通用头部分)
  • 数据部分的头数据XLogRecordBlockHeader
  • 数据部分的头数据XLogRecordDataHeaderShort
  • 一条被插入的元组(严格地说,是一个xl_heap_header结构与完整的插入数据)
  • 结构体 xl_heap_insert(主数据)

XLogRecordBlockHeader包含用于在数据库集群中定位区块的变量(relfilenode,fork编号和区块号),用于指定该元组被插入哪个块中,以及要插入数据部分的长度。

XLogRecordDataHeaderShort存储xl_heap_insert结构的长度,该结构是记录的主要数据。

新的xl_heap_insert只包含两个值:当前元组在块内的偏移量,以及可见性标志。该结构非常简单,因为XLogRecordBlockHeader存储了旧版本中绝大部分数据。

xl_head_insert定义如下:
在这里插入图片描述

3. 检查点记录

检查点记录如图c所示,它由三个数据结构组成:

  • 结构体XLogRecord(通用头部分)
  • 数据部分的头数据XLogRecordDataHeaderShort,包含主数据长度
  • 结构体CheckPoint(主要数据)

结构xl_heap_header在src/include/access/htup.h中定义,CheckPoint结构在src/include/catalog/pg_control.h中定义。

三、 XLOG记录写入

现在我们已经准备好了理解XLOG记录的写入过程,以下列语句执行为例,探索PostgreSQL内幕。

testdb=# INSERT INTO tbl VALUES ('A');

上述语句会调用内部函数exec_simple_query(),其伪代码如下:

exec_simple_query() @postgres.c

(1) ExtendCLOG() @clog.c                  /* 将当前事务的状态"IN_PROGRESS" 写入CLOG */

(2) heap_insert()@heapam.c                /* 插入元组,创建一条xlog记录并调用函数XLogInsert */
(3)   XLogInsert() @xlog.c                /* 9.5开始为xloginsert.c,将插入元组的xlog记录写入WAL缓冲区,更新页面的pd_lsn */

(4) finish_xact_command() @postgres.c     /* 执行提交*/   
      XLogInsert() @xlog.c                /* 9.5开始为xloginsert.c,将该提交行为的xlog记录写入WAL缓冲区 */

(5)   XLogWrite() @xlog.c                 /* 将WAL缓冲区中所有的xlog写入WAL文件 */

(6) TransactionIdCommitTree() @transam.c  /* 在CLOG中将事务状态由"IN_PROGRESS"改为"COMMITTED" on the CLOG */

XLOG记录的写入过程(9.4版本),如下图:
上图XLOG记录的格式是9.4版本的

在这里插入图片描述

四、 WAL写进程

WAL写进程是一个后台进程,用于定期检查WAL缓冲区,并将所有未写入的XLOG记录写入WAL文件。此进程的目的是避免XLOG记录的突发写入。若未启用该进程,在大量提交数据时,XLOG记录的写入可能会成为瓶颈。

WAL写进程默认是启用的,并且无法禁用。但检查间隔可以通过参数wal_writer_delay配置,默认值为200毫秒。

参考

The Internals of PostgreSQL : Chapter 9 Write Ahead Logging — WAL

Postgresql管理系列-第九章 WAL(Write Ahead Logging)介绍_魂醉的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值