首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Postgresql源码(5)Xlog注册

Postgresql源码(5)Xlog注册

作者头像
mingjie
发布2022-05-12 08:18:14
发布2022-05-12 08:18:14
7470
举报

日志拼装流水线

代码语言:javascript
复制
[1] XLogBeginInsert
----> 注册数据
[2] XLogRegisterData:注册生成日志记录的数据,每调一次使用一个rdatas数组位置
[3] XLogRegisterBuffer:注册数据页面相关信息
[4] XLogRegisterBlock
[4] XLogRegisterBufData
...
[5] XLogSetRecordFlags
----> 组装、写入xlog
[6] XLogInsert
    [6.1] XLogRecordAssemble:组装日志记录
    [6.2] XLogInsertRecord:写入WAL BUFFER
    [6.3] XLogResetInsertion:重置日志写入的各种变量

[3] XLogRegisterBuffer

注册数据页面相关信息 注册数据页面相关信息 注册数据页面相关信息 一个页面用一个槽位 一个页面用一个槽位 一个页面用一个槽位 一个槽位对一个registered_buffer 一个槽位对一个registered_buffer 一个槽位对一个registered_buffer

代码语言:javascript
复制
/*
 * For each block reference registered with XLogRegisterBuffer, we fill in
 * a registered_buffer struct.
 */
typedef struct
{
	bool		in_use;			/* is this slot in use? */
	uint8		flags;			/* REGBUF_* flags */
【页面的文件虚拟node】
	RelFileNode rnode;			/* identifies the relation and block */
	ForkNumber	forkno;
	BlockNumber block;
【页面内容指针】
	Page		page;			/* page content */
【XLogRegisterBufferData 注册进来的长度】
	uint32		rdata_len;		/* total length of data in rdata chain */
【XLogRegisterBufferData 注册数据到这个链表】
	XLogRecData *rdata_head;	/* head of the chain of data registered with
								 * this block */
	XLogRecData *rdata_tail;	/* last entry in the chain, or &rdata_head if
								 * empty */

	XLogRecData bkp_rdatas[2];	/* temporary rdatas used to hold references to
								 * backup block data in XLogRecordAssemble() */

	/* buffer to store a compressed version of backup block image */
	char		compressed_page[PGLZ_MAX_BLCKSZ];
} registered_buffer;

页面占用registered_buffer的槽位由手工指定空间,两个页面会使用0和1两个页面,分别占用0和1槽位,例如heap_insert:

代码语言:javascript
复制
		XLogBeginInsert();
		XLogRegisterData((char *) &xlrec, SizeOfHeapInsert);

		xlhdr.t_infomask2 = heaptup->t_data->t_infomask2;
		xlhdr.t_infomask = heaptup->t_data->t_infomask;
		xlhdr.t_hoff = heaptup->t_data->t_hoff;

【insert单次使用一个页面,占用registered_buffer的0号位置】
		XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
		
【注册完了写数据,写什么数据? xl_heap_header{t_infomask2 = ?, t_infomask = ?, t_hoff = 24}】
		XLogRegisterBufData(0, (char *) &xlhdr, SizeOfHeapHeader);
		/* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */
代码语言:javascript
复制
|-------------------------------|       <------ tuple
|           HeapTuple           |
|-------------------------------|       <------ tuple->t_data      header->t_hoff = len(head)
|        HeapTupleHeader        |
|-------------------------------|
|                               |
|                               |
|             DATA              |
|                               |
|                               |
|-------------------------------|

这个XLogRegisterBufData很重要,记录tuple具体变更

代码语言:javascript
复制
【接着写重要的数据!写tuple的实际数据】
【heaptup->t_data这个是把HeapTuple和HeapTupleHeader都跳过去了,指向tuple实际数据】
【heaptup->t_len - SizeofHeapTupleHeader】
【 = sizeof(HeapTupleHeader + DATA) - sizeof(HeapTupleHeader)】
		XLogRegisterBufData(0,
							(char *) heaptup->t_data + SizeofHeapTupleHeader,
							heaptup->t_len - SizeofHeapTupleHeader);


		/* filtering by origin on a row level is much more efficient */
		XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
【写入WAL BUFFER】
		recptr = XLogInsert(RM_HEAP_ID, info);

XLogRegisterBuffer先注册完了才能用XLogRegisterBufData写!

XLogRegisterBuffer做了什了? (1)分配registered_buffers的0号位置 (2)从buffer反推TAG,填充到registered_buffers槽位的page (3)配置flags、rdata_tail、rdata_len

XLogRegisterBufData做了什么? (1)从registered_buffers拿到指定的槽位 (2)从rdatas数组中拿一个位置 (3)数据存到rdatas中的那个位置 (4)把rdatas这个位置链接进registered_buffers中

数据结构展开:

ps.【数组链表的编程技巧】

数组内存连续,申请方便,链表增删速度快,学习下PG的实现。

链表:

代码语言:javascript
复制
typedef struct
{
	...
	XLogRecData *rdata_head;
	XLogRecData *rdata_tail;
	...
} registered_buffer;

【初始化:】
【链表初始化把tail指向head的地址,第一次往TAIL里面写其实就是写HEAD】
【这样可以免去初始化HEAD的动作】
regbuf->rdata_tail = (XLogRecData *) &regbuf->rdata_head;
regbuf->rdata_len = 0;

数组:

代码语言:javascript
复制
typedef struct XLogRecData
{
	struct XLogRecData *next;
	char	   *data;
	uint32		len;
} XLogRecData;

【数组出初始化20个位置】
static XLogRecData *rdatas;
rdatas = MemoryContextAlloc(xloginsert_cxt, sizeof(XLogRecData) * XLR_NORMAL_RDATAS);
max_rdatas = XLR_NORMAL_RDATAS; // 20

【num_rdatas做index依次使用,有严格上限】
	if (num_rdatas >= max_rdatas)
		elog(ERROR, "too much WAL data");
	rdata = &rdatas[num_rdatas++];
【数据存在数组元素里面】
	rdata->data = data;
	rdata->len = len;
【链表首先把第一个元素的next指向下一个】
【然后把之前】
	regbuf->rdata_tail->next = rdata;
	regbuf->rdata_tail = rdata;
	regbuf->rdata_len += len;

流程图:

  1. XLogRegisterBuf执行后:rdata_tail是指向head的,后面XLogRegisterBufData中绿色部分执行时,会把head的next赋值rdatas的+1位置
  2. 然后蓝色部分执行tail的next会变成rdatas+1的位置。
  3. 第二次XLogRegisterBufData执行,红色部分会把rdatas+1的next指针赋值rdatas+2;
  4. 然后执行紫色部分,tail在指向rdatas+2
  5. …重复前面的流程
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-09-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 日志拼装流水线
  • [3] XLogRegisterBuffer
    • ps.【数组链表的编程技巧】
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档