XLOG
一些概念
基本概念
XLOG(也称为WAL日志)是预写日志(Write-Ahead Logging)的一部分。XLOG是一种持久化的、顺序记录的日志,用于记录数据库系统中对数据进行的更改。
具体来说,XLOG用于确保数据库的持久性和一致性。其工作原理如下:
- 当对数据库中的数据进行修改时,首先将数据变更写入XLOG中。
- 接着,将数据变更写入实际的数据文件。
- 事务提交时,不要求发生变化的数据页面罗盘,但必须要求本事务的xlog落盘。
在数据库崩溃或服务器故障时,可以使用XLOG中的信息来恢复数据,确保数据库完整性。因此,XLOG是数据库事务持久性的关键组成部分,它确保了即使在出现意外情况时,数据库也能够在重新启动后恢复到一个一致的状态。
总而言之,XLOG(WAL日志)是PostgreSQL中用于持久化记录数据更改的机制,它是数据库性能、一致性和可靠性的重要保障。
文件管理结构
XLOG日志一条一条的顺序写入XLOG文件。文件是以固定大小的段(segment)的方式组织的。每个 XLOG 段的大小是固定的,通常是 16MB。XLOG 文件是循环使用的,一旦填满一个段,它将被标记为已用,并且新的 XLOG 记录将写入下一个可用的 XLOG 段中。
XLOG 段的组织方式通常是这样的:
-
XLOG 文件名格式:XLOG 文件的命名格式通常是
XLOG+数字+段位置
,例如,XLOG 文件名可能是XLOG000000010000001D
,其中000000010000001D
是 XLOG 段的位置。 -
XLOG 文件目录结构:XLOG 文件通常存储在数据库的数据目录下的
pg_xlog
子目录中。每个数据库都有自己的pg_xlog
目录。 -
XLOG 段的循环使用:一旦一个 XLOG 段被写满,新的 XLOG 记录将被写入下一个可用的 XLOG 段中。当所有 XLOG 段都被写满后,旧的 XLOG 段会被标记为可重用。
-
检查点:定期进行检查点操作来确保部分 XLOG 段中的数据已经写入数据文件中,这样可以释放已经写入数据文件中的 XLOG 段。检查点操作也有助于限制重做日志(WAL)的增长速度。
总的来说,XLOG 文件以固定大小的段的形式组织,通过循环使用这些段,确保了持久化记录数据库更改的机制。这个机制保证了即使在数据库崩溃或服务器故障情况下,都能够进行数据恢复和一致性保障。
16M文件1:
XLog1
XLog2
XLog3
...
16M文件2:
XLog888
XLog889
XLog890
...
LSN(Log Sequence Number)
日志序列号,是一种用于标识事务日志中位置的特殊序号,主要用于在数据库恢复过程中跟踪和管理 xlog 日志文件的位置。
因为XLOG是一条条顺序写入文件的,那么每条xlog的起始位置,也就是在所有xlog文件中的偏移量,就是这条xlog的LSN,所以不难看出 LSN 是一个递增的数字。
对于xlog来说,lsn是它的位置,也是唯一标识;对于页面来说,LSN相当于页面的版本号。
XLOG结构
{XLogRecord} + {XLogRecordBlockHeader}*N + {(Short/Long)XLogRecordDataHeader} + {Block data}*N + {main data}
共5部分,其中第二或四部分不涉及的话可能没有。
数据结构与接口
关键数据结构
struct XLogRecord: 一条XLog
{
uint32 xl_tot_len; xlog的总长度
xl_term;
TransactionId xl_xid; 事务号
XLogRecPtr xl_prev; 前边那条xlog的lsn
RmgrId xl_rmid; xlog的类型
...
xl_crc; CRC校验码
}
这只是个xlog的头部,在这个后面物理空间上会紧跟这剩余的xlog内容
struct XLogRecData: 一条Xlog中的某种数据的容器。
struct XLogRecData {
struct XLogRecData* next;
char* data;
uint32 len;
Buffer buffer;
}
是一个通用的容器,可以存放很多东西。例如xlog本身的一些元信息、具体的某段数据(如insert的某行元组)等。
struct registered_buffer: xlog所操作的页面的注册信息
一条xlog里面相关的页面操作,这个结构用于存放页面的相关元信息,如页面位置、lastlsn。
其中还有一个链表,rdata,存放这个页面上的操作的具体数据,如insert的元组等。一个xlog上可能会有多个操作,例如update,可能会delete+insert元组在同一个页面等。
knl_t_xlog_context: xlog上下文,有很多xlog的相关数据结构。
// 一个数组,构造xlog的时候使用,用于存放xlog所操作的页面的很多元信息。
registered_buffer* registered_buffers;
int max_registered_buffers;
int max_registered_block_id;
// 一个单链表,构造xlog的时候使用,用于存放xlog内的数据内容。
XLogRecData* mainrdata_header;
XLogRecData* mainrdata_last;
uint32 mainrdata_len;
// 一个数组,用于存放registered_buffers的页面数据。物理上是一个数组,逻辑上是好多链表,不同的链表归不同的registered_buffers。
XLogRecData* rdatas;
int max_rdatas;
int num_rdatas;
关键接口
XlogBeginInsert():
做Xlog插入的准备工作,主要是检查状态、初始化一些变量等。
XlogRegisterData(char *data, int len):
创建一个XLogRecData
,注册到链表manrdata之中。
XlogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags, TdeInfo* tdeinfo):
根据buffer构造struct registered_buffer
,注册到registered_buffers数组。
其中block_id是registered_buffers数组的下标。
这里的数据,主要是页面的元信息,例如lsn等。
XlogRegisterBufData(uint8 block_id, Buffer buffer, uint8 flags, TdeInfo* tdeinfo):
根据buffer构造struct registered_buffer
,注册到rdatas数组。
这里的数据指的就是具体的元组了。
XLogInsert():
根据前面接口注册的各种信息,构造出一条xlog,写入xlog缓冲区,返回lsn。
代码流程
检查些有的没的
GetFullPageWriteInfo()
# 将之前注册的所有信息(链表manrdata、registered_buffers数组等),整理成一个链表
XlogRecData* rdt = XLogRecordAssemble()
# 插入xlog到缓冲区,并获取LSN
EndPos = XLogInsertRecord();
# 返回LSN
return EndPos;