[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:重置日志写入的各种变量注册数据页面相关信息 注册数据页面相关信息 注册数据页面相关信息 一个页面用一个槽位 一个页面用一个槽位 一个页面用一个槽位 一个槽位对一个registered_buffer 一个槽位对一个registered_buffer 一个槽位对一个registered_buffer
/*
* 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:
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 */|-------------------------------| <------ tuple
| HeapTuple |
|-------------------------------| <------ tuple->t_data header->t_hoff = len(head)
| HeapTupleHeader |
|-------------------------------|
| |
| |
| DATA |
| |
| |
|-------------------------------|这个XLogRegisterBufData很重要,记录tuple具体变更
【接着写重要的数据!写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中
数据结构展开:

数组内存连续,申请方便,链表增删速度快,学习下PG的实现。
链表:
typedef struct
{
...
XLogRecData *rdata_head;
XLogRecData *rdata_tail;
...
} registered_buffer;
【初始化:】
【链表初始化把tail指向head的地址,第一次往TAIL里面写其实就是写HEAD】
【这样可以免去初始化HEAD的动作】
regbuf->rdata_tail = (XLogRecData *) ®buf->rdata_head;
regbuf->rdata_len = 0;数组:
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;流程图:
