PostgreSQL xlog dump - pg_xlogdump 源码讲解

12 minute read

背景

PostgreSQL 9.3 添加了 pg_xlogdump 的功能, 主要用于解析XLogRecord的信息.

可用于学习了解XLOG.

正文

XLogRecord的数据结构如下 :

src/include/access/xlog.h

00021 /*  
00022  * The overall layout of an XLOG record is:  
00023  *      Fixed-size header (XLogRecord struct)  
00024  *      rmgr-specific data  
00025  *      BkpBlock  
00026  *      backup block data  
00027  *      BkpBlock  
00028  *      backup block data  
00029  *      ...  
00030  *  
00031  * where there can be zero to four backup blocks (as signaled by xl_info flag  
00032  * bits).  XLogRecord structs always start on MAXALIGN boundaries in the WAL  
00033  * files, and we round up SizeOfXLogRecord so that the rmgr data is also  
00034  * guaranteed to begin on a MAXALIGN boundary.  However, no padding is added  
00035  * to align BkpBlock structs or backup block data.  
00036  *  
00037  * NOTE: xl_len counts only the rmgr data, not the XLogRecord header,  
00038  * and also not any backup blocks.  xl_tot_len counts everything.  Neither  
00039  * length field is rounded up to an alignment boundary.  
00040  */  
00041 typedef struct XLogRecord  
00042 {  
00043     uint32      xl_tot_len;     /* total len of entire record */  
00044     TransactionId xl_xid;       /* xact id */  
00045     uint32      xl_len;         /* total len of rmgr data */  
00046     uint8       xl_info;        /* flag bits, see below */  
00047     RmgrId      xl_rmid;        /* resource manager for this record */  
00048     /* 2 bytes of padding here, initialize to zero */  
00049     XLogRecPtr  xl_prev;        /* ptr to previous record in log */  
00050     pg_crc32    xl_crc;         /* CRC for this record */  
00051   
00052     /* If MAXALIGN==8, there are 4 wasted bytes here */  
00053   
00054     /* ACTUAL LOG DATA FOLLOWS AT END OF STRUCT */  
00055   
00056 } XLogRecord;  

用法 :

pg_xlogdump decodes and displays PostgreSQL transaction logs for debugging.  
  
Usage:  
  pg_xlogdump [OPTION] [STARTSEG [ENDSEG]]   
  
General options:  
  -V, --version          output version information, then exit  
  -?, --help             show this help, then exit  
  
Content options:  
  -b, --bkp-details      output detailed information about backup blocks  
  -e, --end=RECPTR       stop reading at log position RECPTR  
  -n, --limit=N          number of records to display  
  -p, --path=PATH        directory in which to find log segment files  
                         (default: ./pg_xlog)  
  -r, --rmgr=RMGR        only show records generated by resource manager RMGR  
                         use --rmgr=list to list valid resource manager names  
  -s, --start=RECPTR     stop reading at log position RECPTR  
  -t, --timeline=TLI     timeline from which to read log records  
                         (default: 1 or the value used in STARTSEG)  
  -x, --xid=XID          only show records with TransactionId XID  

输出解释, 以下条输出为例 :

rmgr: Btree       len (rec/tot):     18/  2374, tx:       1686, lsn: 0/0181D8E8, prev 0/0181CFA0, bkp: 1000, desc: insert: rel 1663/  
16385/12599; tid 1/113  
        backup bkp #0; rel 1663/16385/12599; fork: main; block: 1; hole: offset: 476, length: 5892  

以上输出每个部分的详细解释 :

1. 该XLogRecord的资源管理器.

rmgr: Btree  

取自XLogRecord.xl_rmid.

定义在src/include/access/rmgrlist.h中可以找到. 详见本文参考部分.

2. 该XLogRecord的资源管理器的长度以及该XLogRecord的总长度

len (rec/tot):     18/  2374,  

取自XLogRecord.xl_len和XLogRecord.xl_tot_len

3. 该XLogRecord的事务ID(TransactinoId)

tx:       1686,  

4. 该XLogRecord在xlog虚拟地址空间中的位置.

将XLogRecPtr拆成2个UINT32输出. 分别表示

lsn: 0/0181D8E8,  

xlog文件的命名也与xlog虚拟地址空间有关.

参考 :

http://blog.163.com/digoal@126/blog/static/16387704020129159590908/

http://blog.163.com/digoal@126/blog/static/1638770402012914112949546/

而在PageHeaderData中pd_lsn存储的是该BLOCK对应的最后一次被改变时记录的xlog虚拟地址空间中的下一个位置

pd_lsn	XLogRecPtr	8 bytes	LSN: next byte after last byte of xlog record for last change to this page.  

5. 该XLogRecord的前一条XLogRecord的位置(uint64位虚拟地址空间)

将XLogRecPtr拆成2个UINT32输出.

prev 0/0181CFA0,  

6. 该XLogRecord的disk blocks的备份标记, 每个XLOG Record最多可以记录4个disk blocks.

00046     uint8       xl_info;        /* flag bits, see below */  
00062 /*  
00063  * XLOG uses only low 4 bits of xl_info.  High 4 bits may be used by rmgr.  
00064  */  
00065 #define XLR_INFO_MASK           0x0F  
00066   
00067 /*  
00068  * If we backed up any disk blocks with the XLOG record, we use flag bits in  
00069  * xl_info to signal it.  We support backup of up to 4 disk blocks per XLOG  
00070  * record.  
00071  */  
00072 #define XLR_BKP_BLOCK_MASK      0x0F    /* all info bits used for bkp blocks */  
bkp: 1000,  

7. 描述信息, 根据不同的资源管理器, 描述信息不同.

例如本条记录的资源管理器为Btree.

desc: insert: rel 1663/16385/12599; tid 1/113  

Btree描述信息取自 contrib/pg_xlogdump/nbtdesc.c

out_target(StringInfo buf, xl_btreetid *target)  
{  
        appendStringInfo(buf, "rel %u/%u/%u; tid %u/%u",  
                         target->node.spcNode, target->node.dbNode, target->node.relNode,  
                                         ItemPointerGetBlockNumber(&(target->tid)),  
                                         ItemPointerGetOffsetNumber(&(target->tid)));  
}  

包含表空间/数据库/对象的节点信息.

该索引数据块号码和OFFSET号.

以下是XLogRecord中包含的备份disk block的详细信息. 每个XLogRecord中最多4条.

数据结构如下 :

src/include/access/xlog_internal.h

00030 /*  
00031  * Header info for a backup block appended to an XLOG record.  
00032  *  
00033  * As a trivial form of data compression, the XLOG code is aware that  
00034  * PG data pages usually contain an unused "hole" in the middle, which  
00035  * contains only zero bytes.  If hole_length > 0 then we have removed  
00036  * such a "hole" from the stored data (and it's not counted in the  
00037  * XLOG record's CRC, either).  Hence, the amount of block data actually  
00038  * present following the BkpBlock struct is BLCKSZ - hole_length bytes.  
00039  *  
00040  * Note that we don't attempt to align either the BkpBlock struct or the  
00041  * block's data.  So, the struct must be copied to aligned local storage  
00042  * before use.  
00043  */  
00044 typedef struct BkpBlock  
00045 {  
00046     RelFileNode node;           /* relation containing block */  
00047     ForkNumber  fork;           /* fork within the relation */  
00048     BlockNumber block;          /* block number */  
00049     uint16      hole_offset;    /* number of bytes before "hole" */  
00050     uint16      hole_length;    /* number of bytes in "hole" */  
00051   
00052     /* ACTUAL BLOCK DATA FOLLOWS AT END OF STRUCT */  
00053 } BkpBlock;  

8. 备份数据块的信息, 前面提到了每条XLogRecord中最多可存储4个备份disk block的信息. 从bkp 0开始编号.

backup bkp #0;    

代码如下 :

        if (config->bkp_details)  
        {  
                int                     bkpnum;  
                char       *blk = (char *) XLogRecGetData(record) + record->xl_len;  
  
                for (bkpnum = 0; bkpnum < XLR_MAX_BKP_BLOCKS; bkpnum++)  
                {  
                        BkpBlock        bkpb;  
  
                        if (!(XLR_BKP_BLOCK(bkpnum) & record->xl_info))  
                                continue;  
  
                        memcpy(&bkpb, blk, sizeof(BkpBlock));  
                        blk += sizeof(BkpBlock);  
                        blk += BLCKSZ - bkpb.hole_length;  
  
                        printf("\tbackup bkp #%u; rel %u/%u/%u; fork: %s; block: %u; hole: offset: %u, length: %u\n",  
                                   bkpnum,  
                                   bkpb.node.spcNode, bkpb.node.dbNode, bkpb.node.relNode,  
                                   forkNames[bkpb.fork],  
                                   bkpb.block, bkpb.hole_offset, bkpb.hole_length);  
                }  
        }  

9. 该备份disk block的表空间/数据库/对象节点信息.

rel 1663/16385/12599;  

10. 该备份disk block对应的对象的fork类型, 这个不了解的话可以去看一下相关的手册.

http://www.postgresql.org/docs/devel/static/storage.html

包括main(主存储), fsm(free space map信息), vm(visilibity map信息) 等.

src/include/storage/relfilenode.h

00019 /*  
00020  * The physical storage of a relation consists of one or more forks. The  
00021  * main fork is always created, but in addition to that there can be  
00022  * additional forks for storing various metadata. ForkNumber is used when  
00023  * we need to refer to a specific fork in a relation.  
00024  */  
00025 typedef enum ForkNumber  
00026 {  
00027     InvalidForkNumber = -1,  
00028     MAIN_FORKNUM = 0,  
00029     FSM_FORKNUM,  
00030     VISIBILITYMAP_FORKNUM,  
00031     INIT_FORKNUM  
00032   
00033     /*  
00034      * NOTE: if you add a new fork, change MAX_FORKNUM below and update the  
00035      * forkNames array in catalog.c  
00036      */  
00037 } ForkNumber;  
fork: main;  

11. 该备份disk block的ID编号.

block: 1;  

说明这个disk block是这个对象的第一个数据块.

12. 指这个备份disk block的零空间的位置(假设1个数据块为8KB, 最上面包括了数据块的头文件, 然后是itemID信息等. itemID对应的数据则是从数据块的最底部开始分配的,所以一个数据库的中间部分是空闲的, 也就是初始0的状态, XLogRecord备份的disk block中可以排除这些信息, 减少备份的大小.)

hole: offset: 476, length: 5892  

从这里来看空闲的位置是数据块的476开始的5892个字节. 所以这个部分不需要备份.

数据块的结构如下 :

pic

使用举例

创建测试表

digoal=# create table a (id int primary key, info text);  
CREATE TABLE  

开始事务

digoal=# begin;  
BEGIN  

获取当前事务号

digoal=# select txid_current();  
 txid_current   
--------------  
         1694  
(1 row)  

获取当前XLOG文件名

digoal=# select pg_xlogfile_name(pg_current_xlog_location());  
     pg_xlogfile_name       
--------------------------  
 000000010000000000000004  
(1 row)  

插入数据

digoal=# insert into a values (1,'digoal');  
INSERT 0 1  

提交事务

digoal=# end;  
COMMIT  

获取当前XLOG文件名

digoal=# select pg_xlogfile_name(pg_current_xlog_location());  
     pg_xlogfile_name       
--------------------------  
 000000010000000000000004  
(1 row)  

第一个XLOG文件名传入pg_xlogdump的STARTSEG, 第二个XLOG文件名传入ENDSEG.

下面将要输出以上事务的XLOG信息 :

pgdev@db-172-16-3-150-> pg_xlogdump -b -x 1689 -p /data06/pgdev/pg_root/pg_xlog 000000010000000000000002 000000010000000000000002  
rmgr: Heap        len (rec/tot):     38/    70, tx:       1694, lsn: 0/040334A8, prev 0/04033468, bkp: 0000, desc: insert(init): rel 1663/16385/16410; tid 0/1  
rmgr: Btree       len (rec/tot):     20/    52, tx:       1694, lsn: 0/040334F0, prev 0/040334A8, bkp: 0000, desc: newroot: rel 1663/16385/16416; root 1 lev 0  
rmgr: Btree       len (rec/tot):     34/    66, tx:       1694, lsn: 0/04033528, prev 0/040334F0, bkp: 0000, desc: insert: rel 1663/16385/16416; tid 1/1  
rmgr: Transaction len (rec/tot):     48/    80, tx:       1694, lsn: 0/04033570, prev 0/04033528, bkp: 0000, desc: commit: 2013-02-23 13:48:20.914498 CST; inval msgs: relcache 16416  

描述信息中涉及的rel ID可在数据库中查到 :

digoal=# select pg_relation_filepath('a');  
 pg_relation_filepath   
----------------------  
 base/16385/16410  
(1 row)  
digoal=# select pg_relation_filepath('a_pkey');  
 pg_relation_filepath   
----------------------  
 base/16385/16416  
(1 row)  

表空间信息 :

digoal=# select * from pg_tablespace where oid='1663';  
  spcname   | spcowner | spcacl | spcoptions   
------------+----------+--------+------------  
 pg_default |       10 |        |   
(1 row)  

当然也可以反向查询, 默认表空间取0 :

digoal=# select relname from pg_class where relfilenode=16410 and reltablespace in (0,1663);  
 relname   
---------  
 a  
(1 row)  
digoal=# select relname from pg_class where relfilenode=16416 and reltablespace in (0,1663);  
 relname   
---------  
 a_pkey  
(1 row)  

再解释一下描述信息 :

1. desc: insert(init): rel 1663/16385/16410; tid 0/1

        appendStringInfo(buf, "rel %u/%u/%u; tid %u/%u",  
                         target->node.spcNode, target->node.dbNode, target->node.relNode,  
                                         ItemPointerGetBlockNumber(&(target->tid)),  
                                         ItemPointerGetOffsetNumber(&(target->tid)));  

2. desc: newroot: rel 1663/16385/16416; root 1 lev 0

                                appendStringInfo(buf, "newroot: rel %u/%u/%u; root %u lev %u",  
                                                                 xlrec->node.spcNode, xlrec->node.dbNode,  
                                                                 xlrec->node.relNode,  
                                                                 xlrec->rootblk, xlrec->level);  

btree索引的操作介绍见本文参考部分, 例如可以观察出新插入的ITEM是否是分裂插入的. 例如以下的UUID作为索引的例子, 可以通过XLogRecord来观察是否经常发生分裂.

http://blog.163.com/digoal@126/blog/static/16387704020129249646421/

3. desc: insert: rel 1663/16385/16416; tid 1/1

        appendStringInfo(buf, "rel %u/%u/%u; tid %u/%u",  
                         target->node.spcNode, target->node.dbNode, target->node.relNode,  
                                         ItemPointerGetBlockNumber(&(target->tid)),  
                                         ItemPointerGetOffsetNumber(&(target->tid)));  

4. desc: commit: 2013-02-23 13:48:20.914498 CST; inval msgs: relcache 16416

xact_desc_commit_compact(StringInfo buf, xl_xact_commit_compact *xlrec)  
{  
        int                     i;  
  
        appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));  
  
        if (xlrec->nsubxacts > 0)  
        {  
                appendStringInfo(buf, "; subxacts:");  
                for (i = 0; i < xlrec->nsubxacts; i++)  
                        appendStringInfo(buf, " %u", xlrec->subxacts[i]);  
        }  
}  
......  
                appendStringInfo(buf, "; inval msgs:");  
                for (i = 0; i < xlrec->nmsgs; i++)  
                {  
                        SharedInvalidationMessage *msg = &msgs[i];  
  
                        if (msg->id >= 0)  
                                appendStringInfo(buf, " catcache %d", msg->id);  
                        else if (msg->id == SHAREDINVALCATALOG_ID)  
                                appendStringInfo(buf, " catalog %u", msg->cat.catId);  
                        else if (msg->id == SHAREDINVALRELCACHE_ID)  
                                appendStringInfo(buf, " relcache %u", msg->rc.relId);  
                        /* remaining cases not expected, but print something anyway */  
                        else if (msg->id == SHAREDINVALSMGR_ID)  
                                appendStringInfo(buf, " smgr");  
                        else if (msg->id == SHAREDINVALRELMAP_ID)  
                                appendStringInfo(buf, " relmap");  
                        else  
                                appendStringInfo(buf, " unknown id %d", msg->id);  
                }  

参考

1. contrib/pg_xlogdump/pg_xlogdump.c

/*  
 * Print a record to stdout  
 */  
static void  
XLogDumpDisplayRecord(XLogDumpConfig *config, XLogRecPtr ReadRecPtr, XLogRecord *record)  
{  
        const RmgrDescData *desc = &RmgrDescTable[record->xl_rmid];  
  
        if (config->filter_by_rmgr != -1 &&  
                config->filter_by_rmgr != record->xl_rmid)  
                return;  
  
        if (config->filter_by_xid_enabled &&  
                config->filter_by_xid != record->xl_xid)  
                return;  
  
        config->already_displayed_records++;  
  
        printf("rmgr: %-11s len (rec/tot): %6u/%6u, tx: %10u, lsn: %X/%08X, prev %X/%08X, bkp: %u%u%u%u, desc: ",  
                   desc->rm_name,  
                   record->xl_len, record->xl_tot_len,  
                   record->xl_xid,  
                   (uint32) (ReadRecPtr >> 32), (uint32) ReadRecPtr,  
                   (uint32) (record->xl_prev >> 32), (uint32) record->xl_prev,  
                   !!(XLR_BKP_BLOCK(0) & record->xl_info),  
                   !!(XLR_BKP_BLOCK(1) & record->xl_info),  
                   !!(XLR_BKP_BLOCK(2) & record->xl_info),  
                   !!(XLR_BKP_BLOCK(3) & record->xl_info));  
  
        /* the desc routine will printf the description directly to stdout */  
        desc->rm_desc(NULL, record->xl_info, XLogRecGetData(record));  
  
        putchar('\n');  
  
        if (config->bkp_details)  
        {  
                int                     bkpnum;  
                char       *blk = (char *) XLogRecGetData(record) + record->xl_len;  
  
                for (bkpnum = 0; bkpnum < XLR_MAX_BKP_BLOCKS; bkpnum++)  
                {  
                        BkpBlock        bkpb;  
  
                        if (!(XLR_BKP_BLOCK(bkpnum) & record->xl_info))  
                                continue;  
  
                        memcpy(&bkpb, blk, sizeof(BkpBlock));  
                        blk += sizeof(BkpBlock);  
                        blk += BLCKSZ - bkpb.hole_length;  
  
                        printf("\tbackup bkp #%u; rel %u/%u/%u; fork: %s; block: %u; hole: offset: %u, length: %u\n",  
                                   bkpnum,  
                                   bkpb.node.spcNode, bkpb.node.dbNode, bkpb.node.relNode,  
                                   forkNames[bkpb.fork],  
                                   bkpb.block, bkpb.hole_offset, bkpb.hole_length);  
                }  
        }  
}  

2. src/include/access/rmgrlist.h

00018 /*  
00019  * List of resource manager entries.  Note that order of entries defines the  
00020  * numerical values of each rmgr's ID, which is stored in WAL records.  New  
00021  * entries should be added at the end, to avoid changing IDs of existing  
00022  * entries.  
00023  *  
00024  * Changes to this list possibly need a XLOG_PAGE_MAGIC bump.  
00025  */  
00026   
00027 /* symbol name, textual name, redo, desc, startup, cleanup, restartpoint */  
00028 PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, NULL, NULL, NULL)  
00029 PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, NULL, NULL, NULL)  
00030 PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, NULL, NULL, NULL)  
00031 PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, NULL, NULL, NULL)  
00032 PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, NULL, NULL, NULL)  
00033 PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, NULL, NULL, NULL)  
00034 PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, NULL, NULL, NULL)  
00035 PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, NULL, NULL, NULL)  
00036 PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, NULL, NULL, NULL)  
00037 PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, NULL, NULL, NULL)  
00038 PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, NULL, NULL, NULL)  
00039 PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, btree_xlog_startup, btree_xlog_cleanup, btree_safe_restartpoint)  
00040 PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, NULL, NULL, NULL)  
00041 PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_xlog_startup, gin_xlog_cleanup, gin_safe_restartpoint)  
00042 PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_xlog_startup, gist_xlog_cleanup, NULL)  
00043 PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, NULL, NULL, NULL)  
00044 PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_xlog_startup, spg_xlog_cleanup, NULL)  

3. src/include/access/xlog.h

00041 typedef struct XLogRecord  
00042 {  
00043     uint32      xl_tot_len;     /* total len of entire record */  
00044     TransactionId xl_xid;       /* xact id */  
00045     uint32      xl_len;         /* total len of rmgr data */  
00046     uint8       xl_info;        /* flag bits, see below */  
00047     RmgrId      xl_rmid;        /* resource manager for this record */  
00048     /* 2 bytes of padding here, initialize to zero */  
00049     XLogRecPtr  xl_prev;        /* ptr to previous record in log */  
00050     pg_crc32    xl_crc;         /* CRC for this record */  
00051   
00052     /* If MAXALIGN==8, there are 4 wasted bytes here */  
00053   
00054     /* ACTUAL LOG DATA FOLLOWS AT END OF STRUCT */  
00055   
00056 } XLogRecord;  

4. src/include/c.h

00354 typedef uint32 TransactionId;  

5. src/include/access/nbtree.h

00228 /*  
00229  * All that we need to find changed index tuple  
00230  */  
00231 typedef struct xl_btreetid  
00232 {  
00233     RelFileNode node;  
00234     ItemPointerData tid;        /* changed tuple id */  
00235 } xl_btreetid;  

6. src/include/storage/relfilenode.h

00041 /*  
00042  * RelFileNode must provide all that we need to know to physically access  
00043  * a relation, with the exception of the backend ID, which can be provided  
00044  * separately. Note, however, that a "physical" relation is comprised of  
00045  * multiple files on the filesystem, as each fork is stored as a separate  
00046  * file, and each fork can be divided into multiple segments. See md.c.  
00047  *  
00048  * spcNode identifies the tablespace of the relation.  It corresponds to  
00049  * pg_tablespace.oid.  
00050  *  
00051  * dbNode identifies the database of the relation.  It is zero for  
00052  * "shared" relations (those common to all databases of a cluster).  
00053  * Nonzero dbNode values correspond to pg_database.oid.  
00054  *  
00055  * relNode identifies the specific relation.  relNode corresponds to  
00056  * pg_class.relfilenode (NOT pg_class.oid, because we need to be able  
00057  * to assign new physical files to relations in some situations).  
00058  * Notice that relNode is only unique within a particular database.  
00059  *  
00060  * Note: spcNode must be GLOBALTABLESPACE_OID if and only if dbNode is  
00061  * zero.  We support shared relations only in the "global" tablespace.  
00062  *  
00063  * Note: in pg_class we allow reltablespace == 0 to denote that the  
00064  * relation is stored in its database's "default" tablespace (as  
00065  * identified by pg_database.dattablespace).  However this shorthand  
00066  * is NOT allowed in RelFileNode structs --- the real tablespace ID  
00067  * must be supplied when setting spcNode.  
00068  *  
00069  * Note: in pg_class, relfilenode can be zero to denote that the relation  
00070  * is a "mapped" relation, whose current true filenode number is available  
00071  * from relmapper.c.  Again, this case is NOT allowed in RelFileNodes.  
00072  *  
00073  * Note: various places use RelFileNode in hashtable keys.  Therefore,  
00074  * there *must not* be any unused padding bytes in this struct.  That  
00075  * should be safe as long as all the fields are of type Oid.  
00076  */  
00077 typedef struct RelFileNode  
00078 {  
00079     Oid         spcNode;        /* tablespace */  
00080     Oid         dbNode;         /* database */  
00081     Oid         relNode;        /* relation */  
00082 } RelFileNode;  

7. src/include/access/nbtree.h

00204 /*  
00205  * XLOG records for btree operations  
00206  *  
00207  * XLOG allows to store some information in high 4 bits of log  
00208  * record xl_info field  
00209  */  
00210 #define XLOG_BTREE_INSERT_LEAF  0x00    /* add index tuple without split */  
00211 #define XLOG_BTREE_INSERT_UPPER 0x10    /* same, on a non-leaf page */  
00212 #define XLOG_BTREE_INSERT_META  0x20    /* same, plus update metapage */  
00213 #define XLOG_BTREE_SPLIT_L      0x30    /* add index tuple with split */  
00214 #define XLOG_BTREE_SPLIT_R      0x40    /* as above, new item on right */  
00215 #define XLOG_BTREE_SPLIT_L_ROOT 0x50    /* add tuple with split of root */  
00216 #define XLOG_BTREE_SPLIT_R_ROOT 0x60    /* as above, new item on right */  
00217 #define XLOG_BTREE_DELETE       0x70    /* delete leaf index tuples for a page */  
00218 #define XLOG_BTREE_DELETE_PAGE  0x80    /* delete an entire page */  
00219 #define XLOG_BTREE_DELETE_PAGE_META 0x90        /* same, and update metapage */  
00220 #define XLOG_BTREE_NEWROOT      0xA0    /* new root page */  
00221 #define XLOG_BTREE_DELETE_PAGE_HALF 0xB0        /* page deletion that makes  
00222                                                  * parent half-dead */  
00223 #define XLOG_BTREE_VACUUM       0xC0    /* delete entries on a page during  
00224                                          * vacuum */  
00225 #define XLOG_BTREE_REUSE_PAGE   0xD0    /* old page is about to be reused from  
00226                                          * FSM */  

8. http://blog.163.com/digoal@126/blog/static/1638770402012914112949546/

9. http://www.postgresql.org/docs/devel/static/pgxlogdump.html

Flag Counter

digoal’s 大量PostgreSQL文章入口