上一篇文章讲了下xlog的头部,今天详细讲解下record部分,希望这两篇文章对研究postgresql的xlog的同学有所帮助:
本文来自:http://blog.csdn.net/lengzijian/article/details/7840332
首先看下XLOG日志记录结构:
XLogRecord记录了XLOG的相关控制信息,数据结构如下:
typedef struct XLogRecord { pg_crc32 xl_crc; /* 本条记录的CRC校验码 */ XLogRecPtr xl_prev; /* 日志的前一条记录 */ TransactionId xl_xid; /* 事务ID */ uint32 xl_tot_len; /* 整条记录的总长度*/ uint32 xl_len; /* 组员管理器的数据长度*/ uint8 xl_info; /* 信息标志位 */ RmgrId xl_rmid; /* 资源管理器IDtypedef uint8 RmgrId;*/ } XLogRecord; |
其中,资源管理器ID主要用于日志系统中,数据库系统把各种需要记录日志的数据分类,分配给他们对应的资源管理号,系统在回复或者读取日志记录时,能够很方便地知道该日志记录的元数据属于哪一类,结合信息标志位(xl_info)信息,就能知道数据库对元数据做的是那种操作。共有16中资源(有几项还不清楚是做什么用的):
#define RM_XLOG_ID 0 该条日志记录的是一个检查点信息。 #define RM_XACT_ID 1 该条日志记录的是一个事物的提交或者终止信息 #define RM_SMGR_ID 2 #define RM_CLOG_ID 3 CLOG中某一页的初始化 #define RM_DBASE_ID 4 #define RM_TBLSPC_ID 5 #define RM_MULTIXACT_ID 6 #define RM_RELMAP_ID 7 #define RM_STANDBY_ID 8 #define RM_HEAP2_ID 9 #define RM_HEAP_ID 10 该条日志记录的是对队中元组进行修改的信息 #define RM_BTREE_ID 11 该条日志记录的是对BTree进行修改 #define RM_HASH_ID 12 #define RM_GIN_ID 13 #define RM_GIST_ID 14 #define RM_SEQ_ID 15 |
信息标志位(xl_info)的高四位由资源管理器使用,表示该日志是哪种类型的日志,低四位表示对应的块是否需要备份,对于高四位,信息有如下几种:
/* include/access/xact.h * XLOG allows to store some information in high 4 bits of log * record xl_info field */ #define XLOG_XACT_COMMIT 0x00 //事务提交 #define XLOG_XACT_PREPARE 0x10 //预备 #define XLOG_XACT_ABORT 0x20 //事务取消 #define XLOG_XACT_COMMIT_PREPARED 0x30 //准备提交事务 #define XLOG_XACT_ABORT_PREPARED 0x40 //准备取消事务 #define XLOG_XACT_ASSIGNMENT 0x50 //不详。。。(之后补充) /*include/access/htup.h * WAL record definitions for heapam.c's WAL operations * XLOG allows to store some information in high 4 bitsof 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 //更新元组日志 细心的同学会发现,下面的元组操作和上面的事务操作的编码(0x00)重复了,不要忘记之前我们说过,要通过xl_rmid字段来判断属于何种操作:先判断属于那种操作,在做具体的操作内容。 |
低四位中只用了三位,具体如下(可以看到最后一位没有用到):
/*include/access/xlog.h * If we backed up any disk blocks with the XLOG record,we use flag bits in * xl_info to signal it. We support backup of up to 3 disk blocks per XLOG * record. */ #define XLR_BKP_BLOCK_MASK 0x0E /* all info bits used for bkp blocks */ #define XLR_MAX_BKP_BLOCKS 3 #define XLR_SET_BKP_BLOCK(iblk) (0x08 >> (iblk)) #define XLR_BKP_BLOCK_1 XLR_SET_BKP_BLOCK(0) /* 0x08 */ #define XLR_BKP_BLOCK_2 XLR_SET_BKP_BLOCK(1) /* 0x04 */ #define XLR_BKP_BLOCK_3 XLR_SET_BKP_BLOCK(2) /* 0x02 */ |
日志记录数据信息:
rmgr data 数据被XLogInsert()函数写入,有一个或多个XlogRecData数据结构组成,当有多个XLogRecData结构体时有两种情况:1.源数据没有在内存上物理相邻;2.数据在多个缓冲区中被指定。
如果buffer有效,那么XLOG将会检查buffer是否必须备份(即,是否该buffer自最后一次checkpoint以来,第一次被更改)。如果是这样,那么整个页面内容会被附加到XLOG日志中,同时,XLOG在标志位xl_info中设置XLR_BKP_BLOCK_X位。注:当buffer备份后,我们不能够通过XLogRecData结构体插入数据到XLOG记录中,因为我们假定他已经在buffer中了,因此rmgr的redo操作必须注意XLR_BKP_BLOCK_X的值,以便指导XLOG记录中到底存的是什么。
如果buffer有效,调用者必须设置buffer_std(缓冲区存储标准),来表明页面是否用标准pd_lower/pd_upper头字段。
日志记录中的数据信息存储在结构XlogRecData中(结构体如下):
typedef struct XLogRecData { char *data; /* 资源管理器数据 */ uint32 len; /* 资源管理器数据的长度 */ Buffer buffer; /* 该数据涉及的缓冲区*/ bool buffer_std; /* 缓冲区存储标准 */ struct XLogRecData *next; /* 下一个节点指针 */ } XLogRecData; |
这里保存了所有操作信息,在阅读了xlogdump源码之后发现,读取xlog记录不需要XLogRecData结构体,例如读取插入操作时,只需要调用xl_heap_insert结构体就可以取出数据。
例如执行了一次insert,用xlogdump可以读出下面的代码(同样的updata、delete以及事务操作commit和abort都在改数据内):
INSERT: 2 row(s) found in the table `t_user'. //有两个字段在表“t_user”中 INSERT: column 0,name userid,type 1043,value '14541'//包括字段名,字段类型,字段的值都会在数据中取出,如果想了解具体细节可以看xlogdump的源码。 INSERT: column 1,name name,value 'lengzijian' |
XLOG记录中的备份数据块的头部信息保存在BkpBlock中,数据结构如下:
typedef struct BkpBlock { RelFileNode node; /* 表节点*/ ForkNumber fork; /* 关系分支*/ BlockNumber block; /* 块数 */ uint16 hole_offset; /* "hole"偏移值 */ uint16 hole_length; /* "hole"长度 */ /* 实际的块数据在结构体之后 */ } BkpBlock; |
在用xlogdump工具时,发现一个问题,就是更新或插入时,有时不能够打印出statements,例如:
lengzijian-----------record->xl_len[21]/SizeOfHeapUpdate:[28]/SizeOfHeapHeader:[5] //这里我打印出他的判断信息,由于(21 – 28 – 5)的无符号型大于MaxHeapTupleSize //所以退出,没有打印出statements. [cur:0/4C6F0960,xid:5215584,rmid:10(Heap),len/tot_len:21/2781,info:9,prev:0/4C6F0910] insert: s/d/r:pg_default/lengzijian/t_user blk/off:758/61 header: none [cur:0/4C6F0960,prev:0/4C6F0910] bkpblock[1]: s/d/r:pg_default/lengzijian/t_user blk:758 hole_off/len:268/5484 //根据如上分析:是由于已经把数据备份到bkpblock[1]中,通过观察日志也可以看到,在之后,xlogdump把bkpblock也打印出来,说明xl_info的后低四位被设置了。 |