PostgreSQL的存储系统二:REDOLOG文件存储结构

Pg XLOG 文件(常说的 REDOLOG )名字的命名方法是在 XLogFileName 宏里定义的,分别由时间线 ID 、日志 ID 、段 ID 的八位 16 进制数依次构成。例如 00000001000000010000008F

#define XLogFileName (fname, tli, log, seg)    \

    snprintf(fname, MAXFNAMELEN, "%08X%08X%08X" , tli, log, 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 。下面是这两个结构定义:

 

typedef struct XLogLongPageHeaderData

{

    XLogPageHeaderData std ;     /* 页头结构 */

    uint64      xlp_sysid ;    /* 控制文件 pg_control 里的系统标识符 */

    uint32      xlp_seg_size ; /* XLOG 文件大小 */

    uint32      xlp_xlog_blcksz ;  /* XLOG 文件页面大小 */

} XLogLongPageHeaderData ;

 

XLOG 文件里的页面头结构

typedef struct XLogPageHeaderData

{

    uint16      xlp_magic ;    /* WAL 版本指示器 */

    uint16      xlp_info ;     /* flag bits, see below */

    TimeLineID xlp_tli ;      /* 本页第一个记录的时间线 */

    XLogRecPtr xlp_pageaddr ; /* 本页 XLOG 位置 */

} XLogPageHeaderData ;

    XLOG 文件里的页面头结构 XLogPageHeaderData 的成员 xlp_magic 表示 WAL 版本号。成员 xlp_tli 表示本页第一个记录的时间线,成员 xlp_pageaddr 表示本页的 XLOG 位置。成员 xlp_info 表示紧跟页头的记录是否是跨页记录的一部分,或者该页头结构 XLogPageHeaderData 是否在文件头结构 XLogLongPageHeaderData 里。其值定义见下面:

当记录跨页时,在后面的页头里设置标志

#define XLP_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 结构定义如下:

typedef struct XLogRecord

{

    pg_crc32    xl_crc ;           /* 该记录的 CRC */

    XLogRecPtr xl_prev ;      /* xlog 里前一个 XLogRecord 的指针 */

    TransactionId xl_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

#define XLOG_CHECKPOINT_ONLINE         0x10

#define XLOG_NOOP                  0x20

#define XLOG_NEXTOID               0x30

#define XLOG_SWITCH                    0x40

#define XLOG_BACKUP_END                0x50

#define XLOG_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

#define XLOG_XACT_COMMIT_PREPARED  0x30

#define XLOG_XACT_ABORT_PREPARED   0x40

#define XLOG_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

typedef struct XLogContRecord

{

    uint32      xl_rem_len ;       /* 记录剩余数据总长度 */

} XLogContRecord ;

 

 

3

<rmgr-specific data> 可能是事务状态定义等,例如结构 xl_xact_commit xl_xact_abort

 

typedef struct xl_xact_commit

{

    TimestampTz xact_time ;      /* 提交时间 */

    uint32      xinfo ;        /* 信息位 */

    int         nrels ;        /* 关系文件数 */

    int         nsubxacts ;    /* 子事务 XID */

    int         nmsgs ;        /* 共享失效信息数 */

    Oid         dbId ;         /* 数据库 ID */

    Oid         tsId ;         /* 数据库表空间 ID */

    /* 提交时要 drop 的关系文件节点数组 */

    RelFileNode xnodes [1];      /* 变长数组 */

    /* 后面跟已提交子事务 ID 数组 */

    /* 后面跟共享失效消息数组 */

} xl_xact_commit ;

 

typedef struct xl_xact_abort

{

    TimestampTz xact_time ;      /* 退出时间 */

    int         nrels ;        /* 关系文件节点数 */

    int         nsubxacts ;    /* 子事务 XID */

    /* 退出时要 drop 的关系文件节点数组 */

    RelFileNode xnodes [1];      /* 变长数组 */

    /* 后面跟以退出的子事务 XID 数组 */

} xl_xact_abort ;

 

4

<BkpBlock> 表示 BkpBlock 结构,是 跟在 XLOG 记录 XLogRecord 后面的备份块头信息。 XLOG 代码知道 PG 数据页面中间常常包含一个无用的“洞”(“ hole ”——未使用的空间),其只包含值是 0 的字节。如果这个洞的长度大于 0 hole_length > 0 ),实际的跟在结构 BkpBlock 后面的块数据量是 BLCKSZ - hole_length 个字节

typedef struct BkpBlock

{

    RelFileNode node ;        /* 包含该块的关系文件 */

    ForkNumber fork ;         /* 关系分支 */

    BlockNumber block ;          /* 块数 */

    uint16      hole_offset ;  /* "hole" 前字节数 */

    uint16      hole_length ;  /* "hole" 的字节数 */

 

    /* 实际的块数据跟在结构后面 */

} BkpBlock ;

 

5

<XLogRecData> 表示 XLogRecData 结构。要写入 XLOG 日志文件的资源管理器数据, 由一或多个 XLogRecData 结构定义。

typedef struct 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 来判定数据是检查点。检查点的定义见下面。

 

typedef struct CheckPoint

{

    XLogRecPtr redo ;         /* 开始创建一个检查点时下一个 XLOG 记录的位置 */

    TimeLineID ThisTimeLineID ; /* 当前时间线 */

    uint32      nextXidEpoch ; /* 下一个事务 ID 的高排序位 */

    TransactionId nextXid ;      /* 下一个空闲事务 ID */

    Oid         nextOid ;      /* 下一个空闲 OID */

    MultiXactId nextMulti ;      /* 下一个空闲多事务 ID */

    MultiXactOffset nextMultiOffset ;   /* next free MultiXact offset */

    TransactionId oldestXid ; /* cluster-wide minimum datfrozenxid */

    Oid         oldestXidDB ;  /* database with minimum datfrozenxid */

    pg_time_t   time ;         /* 检查点时间戳 */

 

    /*

仍在运行的最早的事务 ID XID )。只有在从一个在线检查点初始化热备模式时才需要,以使在 GUC 参数 wal_level hot_standby 时我们不用为在线检查点计算运行最早的 XID 。否则设置为常量 InvalidTransactionId

      */

    TransactionId oldestActiveXid ;

} CheckPoint ;


------------
转载请著明出处,来自博客:
blog.csdn.net/beiigang
beigang.iteye.com

猜你喜欢

转载自beigang.iteye.com/blog/1565121