PostgreSQL xlog dump - pg_xlogdump 源码讲解
背景
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个字节. 所以这个部分不需要备份.
数据块的结构如下 :
使用举例
创建测试表
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