PostgreSQL的存储系统二:REDOLOG文件存储结构
Pg XLOG文件(常说的REDOLOG)名字的命名方法是在XLogFileName宏里定义的,分别由时间线ID、日志ID、段ID的八位16进制数依次构成。例如00000001000000010000008F。 #define XLogFileName(fname,tli,log,seg) snprintf(fname,MAXFNAMELEN,"%08X%08X%08X",seg) 不同的段ID对应不同的物理XLOG日志文件,日志ID是逻辑概念,有多个物理XLOG日志文件组成。 Pg XLOG文件(常说的REDOLOG)的大小初始值是在configure.in和configure文件里设置的,默认大小是16M。大小设置形式如下 wal_segsize=16 XLOG_SEG_SIZE (${wal_segsize} * 1024 * 1024)=16M 这个XLOG_SEG_SIZE是XLOG日志文件的段大小,就是一个物理的日志文件的大小。一个逻辑的XLOG日志文件大小是4Gb。
Pg XLOG文件的存储格式大致如下: <PageHeaderData> <XLogRecord> <rmgr-specific data> <BkpBlock> <XLogRecData>里面包括<CheckPoint>等 <BkpBlock> <XLogRecData> <BkpBlock> <XLogRecData> ……
1 先看<PageHeaderData>,这个根据不同情况对应XLogLongPageHeaderData或XLogPageHeaderData结构。XLOG文件头用XLogLongPageHeaderData结构和一些合适场合,XLogPageHeaderData用在文件里的页面头。结构XLogLongPageHeaderData的第一个成员是结构XLogPageHeaderData。下面是这两个结构定义:
typedefstruct XLogLongPageHeaderData { XLogPageHeaderDatastd; /*页头结构*/ uint64 xlp_sysid; /* 控制文件pg_control里的系统标识符*/ uint32 xlp_seg_size; /* XLOG文件大小 */ uint32 xlp_xlog_blcksz; /* XLOG文件页面大小 */ } XLogLongPageHeaderData;
XLOG文件里的页面头结构 typedefstruct XLogPageHeaderData { uint16 xlp_magic; /* WAL版本指示器 */ uint16 xlp_info; /* flag bits,see below */ TimeLineIDxlp_tli; /* 本页第一个记录的时间线*/ XLogRecPtrxlp_pageaddr; /* 本页XLOG位置*/ } XLogPageHeaderData; XLOG文件里的页面头结构XLogPageHeaderData的成员xlp_magic表示WAL版本号。成员xlp_tli表示本页第一个记录的时间线,成员xlp_pageaddr表示本页的XLOG位置。成员xlp_info表示紧跟页头的记录是否是跨页记录的一部分,或者该页头结构XLogPageHeaderData是否在文件头结构XLogLongPageHeaderData里。其值定义见下面: 当记录跨页时,在后面的页头里设置标志 #defineXLP_FIRST_IS_CONTRECORD 0x0001 这个指明是长页头(多用于文件第一页) #define XLP_LONG_HEADER 0x0002 在xlp_info中定义的所有标志位(用于头的有效性检查) #define XLP_ALL_FLAGS 0x0003 如果成员xlp_info的值是XLP_FIRST_IS_CONTRECORD,表示页头后面跟的是一个跨页结构的一部分。 XLOG文件头结构XLogLongPageHeaderData的成员xlp_seg_size表示XLOG文件大小,默认是16M。在configure.in和configure文件里设置形式如下 wal_segsize=16 XLOG_SEG_SIZE (${wal_segsize} * 1024 * 1024)=16M 成员xlp_xlog_blcksz表示文件里的页面大小,默认是8k。
2 <XLogRecord>根据情况不同,可能是XLogRecord或XLogContRecord结构,只有在上一个页面最后一个记录是跨页的情况下,页面头的记录前面才用XLogContRecord结构,表示该记录不是一个新记录,而是上一个记录的一部分。XLogRecord结构代表一个新的XLOG/REDOLOG记录,其后跟着实际的XLOG数据,XLogRecord结构定义如下: typedefstruct XLogRecord { pg_crc32 xl_crc; /* 该记录的CRC */ XLogRecPtrxl_prev; /* xlog里前一个XLogRecord的指针*/ TransactionIdxl_xid; /* 该记录事务ID */ uint32 xl_tot_len; /* 整个记录长度 */ uint32 xl_len; /* XLOG资源管理器数据长度 */ uint8 xl_info; /* 标志位 */ RmgrId xl_rmid; /* 本记录的资源管理器ID */ } XLogRecord; XLOG资源管理器时,其成员xl_info值可以是: #define XLOG_CHECKPOINT_SHUTDOWN 0x00 #defineXLOG_CHECKPOINT_ONLINE 0x10 #define XLOG_NOOP 0x20 #define XLOG_NEXTOID 0x30 #define XLOG_SWITCH 0x40 #define XLOG_BACKUP_END 0x50 #defineXLOG_PARAMETER_CHANGE 0x60 #define XLOG_RESTORE_POINT 0x70
XACT资源管理器时,其成员xl_info高4位储存如下信息: #define XLOG_XACT_COMMIT 0x00 #define XLOG_XACT_PREPARE 0x10 #define XLOG_XACT_ABORT 0x20 #defineXLOG_XACT_COMMIT_PREPARED 0x30 #defineXLOG_XACT_ABORT_PREPARED 0x40 #defineXLOG_XACT_ASSIGNMENT 0x50
XLOG仅使用xl_info的低4位,高4为由资源管理器rmgr使用 #define XLR_INFO_MASK 0x0F
如果用XLOG记录备份任一磁盘块(数据文件块吧),用其成员xl_info标志位记录,支持每个XLOG记录3个磁盘块的备份,使用xl_info标志的低1、2、3位 #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 */
如果已备份块能从XLOG的压缩版本中被安全删除,设置Xl_info的0位(这就是,备份它们仅是为了防止写部分页(partial-page-write)问题,并且不保证PITR恢复的一致性)。压缩算法将需要从这些块中解析出数据以创建一个相同的非全页XLOG记录。 #define XLR_BKP_REMOVABLE 0x01
成员xl_rmid记录资源管理器ID,就是资源种类ID,资源管理器的资源种类包括如下16种(不包含最后一个RM_MAX_ID,这个只是记录最大资源种类数): #define RM_XLOG_ID 0 #define RM_XACT_ID 1 #define RM_SMGR_ID 2 #define RM_CLOG_ID 3 #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 #define RM_HASH_ID 12 #define RM_GIN_ID 13 #define RM_GIST_ID 14 #define RM_SEQ_ID 15 #define RM_MAX_ID RM_SEQ_ID 当页头记录是跨页记录的一部分时,在XLogLongPageHeaderData结构的后面跟着XLogContRecord结构。原则是XLogRecord记录头从不被分到多个页,如果页尾空间小于SizeOfXLogRecord,就弃之不用,直接使用下一个页面。如果在一个记录在一个页面没写完,在下一个页头的XLogLongPageHeaderData结构后面用XLogContRecord结构,此时XLogLongPageHeaderData结构的成员xlp_info的值是XLP_FIRST_IS_CONTRECORD。 typedefstruct XLogContRecord { uint32 xl_rem_len; /* 记录剩余数据总长度 */ } XLogContRecord;
3 <rmgr-specificdata>可能是事务状态定义等,例如结构xl_xact_commit、xl_xact_abort等
typedefstruct xl_xact_commit { TimestampTzxact_time; /* 提交时间 */ uint32 xinfo; /* 信息位 */ int nrels; /* 关系文件数 */ int nsubxacts; /* 子事务XID数 */ int nmsgs; /* 共享失效信息数 */ Oid dbId; /* 数据库ID */ Oid tsId; /* 数据库表空间ID */ /* 提交时要drop的关系文件节点数组*/ RelFileNodexnodes[1]; /* 变长数组 */ /* 后面跟已提交子事务ID数组 */ /* 后面跟共享失效消息数组*/ } xl_xact_commit;
typedefstruct xl_xact_abort { TimestampTzxact_time; /* 退出时间 */ int nrels; /* 关系文件节点数 */ int nsubxacts; /* 子事务XID数 */ /* 退出时要drop的关系文件节点数组 */ RelFileNodexnodes[1]; /*变长数组*/ /* 后面跟以退出的子事务XID数组 */ } xl_xact_abort;
4 <BkpBlock>表示BkpBlock结构,是跟在XLOG记录XLogRecord后面的备份块头信息。XLOG代码知道PG数据页面中间常常包含一个无用的“洞”(“hole”——未使用的空间),其只包含值是0的字节。如果这个洞的长度大于0(hole_length >0),实际的跟在结构BkpBlock后面的块数据量是BLCKSZ -hole_length个字节 typedefstruct BkpBlock { RelFileNodenode; /* 包含该块的关系文件 */ ForkNumberfork; /* 关系分支 */ BlockNumberblock; /* 块数 */ uint16 hole_offset; /* "hole"前字节数 */ uint16 hole_length; /* "hole"的字节数 */
/* 实际的块数据跟在结构后面 */ } BkpBlock;
5 <XLogRecData>表示XLogRecData结构。要写入XLOG日志文件的资源管理器数据,由一或多个XLogRecData结构定义。 typedefstruct XLogRecData { char *data; /* 资源管理器包含数据的开始 */ uint32 len; /* 资源管理器包含数据的长度 */ Buffer buffer; /* 有相应数据的buffer,如果有的话 */ bool buffer_std; /* buffer是否有标准pd_lower/pd_upper头 */ struct XLogRecData *next; /* 链里的下一个结构 */ } XLogRecData; pd_lower页面开始位置与未分配空间开头的字节偏移,pd_upper与未分配空间结尾的字节偏移,LSN:最后修改这个页面的 xlog 记录最后一个字节后面第一个字节。
XLogRecData结构里记录各种数据库操作数据,其中典型的,数据是一个检查点(CheckPoint struct)。根据XLogRecord的成员xl_rmid是否等于RM_XLOG_ID以及的成员xl_info是否等于XLOG_CHECKPOINT_SHUTDOWN或XLOG_CHECKPOINT_ONLINE来判定数据是检查点。检查点的定义见下面。
typedefstruct CheckPoint { XLogRecPtrredo; /*开始创建一个检查点时下一个XLOG记录的位置*/ TimeLineIDThisTimeLineID; /*当前时间线*/ uint32 nextXidEpoch; /*下一个事务ID的高排序位 */ TransactionIdnextXid; /* 下一个空闲事务ID */ Oid nextOid; /* 下一个空闲OID */ MultiXactIdnextMulti; /* 下一个空闲多事务ID */ MultiXactOffsetnextMultiOffset; /* next free MultiXact offset */ TransactionIdoldestXid; /* cluster-wide minimum datfrozenxid */ Oid oldestXidDB; /* database with minimum datfrozenxid*/ pg_time_t time; /* 检查点时间戳 */
/* 仍在运行的最早的事务ID(XID)。只有在从一个在线检查点初始化热备模式时才需要,以使在GUC参数wal_level是hot_standby时我们不用为在线检查点计算运行最早的XID。否则设置为常量InvalidTransactionId。 */ TransactionIdoldestActiveXid; } CheckPoint;
------------ 转载请著明出处,来自博客: blog.csdn.net/beiigang beigang.iteye.com(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |