概括起来,XLOG日志分为多个XLOG逻辑日志文件,每个逻辑日志文件包含多个XLOG段文件,每个XLOG段文件包含多个XLOG日志页:

  • 每个XLOG逻辑日志文件都有一个ID

    • 即LSN中的逻辑日志文件号
  • 实际XLOG被分为pg_xlog目录下多个大小为16MB的段文件
    • 文件名由时间线TimeLineID(8位16进制)、逻辑日志文件号(8位16进制)和段文件ID(8位16进制)组成
  • 每个段文件分为多个8KB的页(块)
    • 每个页包含一个头部,头部信息之后才是真正的XLOG日志记录

其中,值得注意的是,每个XLOG段文件大小可以在编译时使用–with-wal-segsize参数来指定,每页的大小可以在编译的时使用–with-wal-blocksize参数来指定,接下来主要介绍XLOG日志每页的组织形式。

XLOG日志页的组织形式

在PostgreSQL中,XLOG日志页可以分为以下几部分:

组成部分 具体含义
PageHeaderData XLOG日志页面头部信息
XLogRecord XLog日志记录的头部信息
Data of RMGR 资源管理器的数据,长度xl_len
Backup Block 0 备份数据块头部BkpBlock + 块大小的备份数据
Backup Block 1 备份数据块头部BkpBlock + 块大小的备份数据
Backup Block 2 备份数据块头部BkpBlock + 块大小的备份数据
Backup Block 3 备份数据块头部BkpBlock + 块大小的备份数据

XLOG日志页头部信息

每个XLOG日志页分为页面头部信息和日志记录,其头部信息XLogPageHeaderData结构如下:

typedef struct XLogPageHeaderData
{uint16     xlp_magic;      /* 校验位,用于识别不同的XLOG版本 */uint16        xlp_info;       /* flag bits, see below */TimeLineID    xlp_tli;        /* 页面第一条记录的时间线 */XLogRecPtr xlp_pageaddr;   /* XLOG页面的首地址 */uint32      xlp_rem_len;    /* 前XLOG页面最后一条记录剩余的长度 */
} XLogPageHeaderData;

其中,xlp_info是标志位:

  • 0x0001表示该页面包含一个跨页面的记录(上个页面的最后一条记录)
  • 0x0002表示该页面为段文件的首个页面,头部是一个长头部
  • 0x0004表示该页面备份数据块是可选的

如果当前的页面没有足够的空间来存储一个XLOG日志记录,系统允许将剩余的数据存储到下一个页面,但是XLog日志记录的头部信息,即后文中的XLogRecord是不允许分开存储到两个不同的页面的。

如果该页面为段文件的首个页面,除了上面的标准页面头部信息外,还增加一个长头部用来更精确地定位文件,即XLogLongPageHeaderData:

typedef struct XLogLongPageHeaderData
{XLogPageHeaderData std;        /* 标准页面头部信息 */uint64        xlp_sysid;      /* pg_control 中的系统标识符*/uint32       xlp_seg_size;   /* 段的尺寸 */uint32        xlp_xlog_blcksz;    /* 页(块)的尺寸*/
} XLogLongPageHeaderData;

XLOG日志记录的头部信息

每个XLOG日志页面头部之后才是真正的XLOG日志记录,XLogRecord记录了XLOG的相关数据信息,具体结构如下:

typedef struct XLogRecord
{uint32     xl_tot_len;     /* 整条记录的总长度*/TransactionId xl_xid;      /* 事务ID */XLogRecPtr    xl_prev;        /* 上条XLOG日志记录的位置(LSN) */uint8     xl_info;        /* flag bits, see below */RmgrId        xl_rmid;        /* 资源管理器ID *//* 2 bytes of padding here, initialize to zero */pg_crc32c xl_crc;         /* 本记录的CRC校验码 *//* XLogRecordBlockHeaders and XLogRecordDataHeader follow, no padding */
} XLogRecord;

其中,xl_rmid表示资源管理器ID,在PostgreSQL中,资源管理器根据资源种类,可以分为17类,其分别的ID按照以下顺序分别为0-16:

PG_RMGR(RM_XLOG_ID, "XLOG", xlog_redo, xlog_desc, NULL, NULL)
PG_RMGR(RM_XACT_ID, "Transaction", xact_redo, xact_desc, NULL, NULL)
PG_RMGR(RM_SMGR_ID, "Storage", smgr_redo, smgr_desc, NULL, NULL)
PG_RMGR(RM_CLOG_ID, "CLOG", clog_redo, clog_desc, NULL, NULL)
PG_RMGR(RM_DBASE_ID, "Database", dbase_redo, dbase_desc, NULL, NULL)
PG_RMGR(RM_TBLSPC_ID, "Tablespace", tblspc_redo, tblspc_desc, NULL, NULL)
PG_RMGR(RM_MULTIXACT_ID, "MultiXact", multixact_redo, multixact_desc, NULL, NULL)
PG_RMGR(RM_RELMAP_ID, "RelMap", relmap_redo, relmap_desc, NULL, NULL)
PG_RMGR(RM_STANDBY_ID, "Standby", standby_redo, standby_desc, NULL, NULL)
PG_RMGR(RM_HEAP2_ID, "Heap2", heap2_redo, heap2_desc, NULL, NULL)
PG_RMGR(RM_HEAP_ID, "Heap", heap_redo, heap_desc, NULL, NULL)
PG_RMGR(RM_BTREE_ID, "Btree", btree_redo, btree_desc, NULL, NULL)
PG_RMGR(RM_HASH_ID, "Hash", hash_redo, hash_desc, NULL, NULL)
PG_RMGR(RM_GIN_ID, "Gin", gin_redo, gin_desc, gin_xlog_startup, gin_xlog_cleanup)
PG_RMGR(RM_GIST_ID, "Gist", gist_redo, gist_desc, gist_xlog_startup, gist_xlog_cleanup)
PG_RMGR(RM_SEQ_ID, "Sequence", seq_redo, seq_desc, NULL, NULL)
PG_RMGR(RM_SPGIST_ID, "SPGist", spg_redo, spg_desc, spg_xlog_startup, spg_xlog_cleanup)

其中,上述引用代码中PG_RMGR函数的参数依次为:

参数名称 具体含义
symname 资源管理器ID
name 资源名称
redo redo恢复函数
desc 描述函数
startup 启动函数
cleanup 清理函数

在PostgreSQL中,用xl_rmid和xl_info高4位来唯一地标示该XLOG日志记录对应的数据库操作,例如事务资源管理器(RM_XACT_ID),对应XLogRecord中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
#define XLOG_XACT_COMMIT_COMPACT    0x60

例如元组管理器(RM_HEAP_ID),对应xl_info的高4位:

#define XLOG_HEAP_INSERT       0x00
#define XLOG_HEAP_DELETE        0x10
#define XLOG_HEAP_UPDATE        0x20
/* 0x030 is free, was XLOG_HEAP_MOVE */
#define XLOG_HEAP_HOT_UPDATE    0x40
#define XLOG_HEAP_NEWPAGE       0x50
#define XLOG_HEAP_LOCK          0x60
#define XLOG_HEAP_INPLACE       0x70

xl_info字段是个xl_info低4位表示当前XLOG记录数据块备份的情况:

#define XLR_BKP_BLOCK_MASK      0x0F    /* all info bits used for bkp blocks */
#define XLR_MAX_BKP_BLOCKS      4
#define XLR_BKP_BLOCK(iblk)     (0x08 >> (iblk))      /* iblk in 0..3 */

XLogRecord的xl_crc记录XLOG日志记录的CRC校验,保证写入到磁盘的XLOG记录都是完整的,如果应用不完整的日志记录,PostgreSQL会报错。

XLOG日志记录的资源管理器数据

XLOG日志记录的资源管理器数据由一系列XLogRecData结构体链表组成,之所以要用XLogRecData链,是因为在所要处理的日志记录实体数据在内存空间可能不是连续存储的,而且数据可能分布在多个缓冲区内,需要用XlogRecData链表将它们组织起来。XlogRecData数据结构如下:

typedef struct XLogRecData
{char      *data;   /*资源管理器包含数据的开始*/uint32      len;        /*资源管理器包含的数据大小*/Buffer      buffer; /*如果有buffer指明第几个缓冲区*/bool       buffer_std; /*是否含有标准的pd_lower/pd_upper结构*/struct XLogRecData *next; /*指向下一个结构体*/
} XLogRecData;

其中,buffer_std该值为true,则容许XLOG释放备份页的空闲空间,空闲空间由pd_lower和pd_upper限定:

  • pd_lower表示页面起始位置与未分配空间开头的字节偏移
  • pd_upper表示页面末尾位置与未分配空间末尾的字节偏移

XLogRecData中data保存每条XLOG日志记录中的数据信息,以INSERT、UPDATE、DELETE为例,XLogRecData中data的大体内容如下(该图引自《Internals Of PostgreSQL Wal》):

备份数据块

备份数据块包含一个头部信息BkpBlock和一块大小的备份数据,其中BkpBlock结构如下:

typedef struct BkpBlock
{RelFileNode  node; /* 用于唯一标示该块所属的关系表,包括表空间OID,数据库OID,关系表OID等*/ForkNumber   fork;      /*一个关系表在存储上可能由多个分支组成,每个分支以文件单独存储,RelFileNode对应关系表的分支号*/BlockNumber  block;        /*对应块的块号*/uint16        hole_offset;    /*空洞偏移量*/uint16     hole_length;    /* 空洞长度*/
} BkpBlock;

如果需要备份的块存在空洞,则备份的时候只记录这个空洞的偏移量和长度,但没有实际备份它,从而提高备份效率。

XLOG record 结构

一条日志记录的的组织形式如下所示(以下分析基于RDS for PostgreSQL,即9.4版本):

 *        Fixed-size header (XLogRecord struct) /*日志记录头*/*        rmgr-specific data /*资源管理器数据,对应操作的对象,譬如元组的id等内容*/*        BkpBlock/*备份块块头*/*        backup block data/*操作的一些数据,如更新元组时,要更新的新值存储在这个区域*/*        BkpBlock*        backup block data*        ...

为了更好地探究堆表INSERT操作对应XLOG record 的内容,我们创建一个简单的TABLE,并执行INSERT操作:

create table test(id int);
insert into test values(3);

XLogInsert函数

在执行INSERT操作的时候,PostgreSQL会调用heap_insert函数,其中会调用XLogInsert去插入对应的XLOG record:

recptr = XLogInsert(RM_HEAP_ID, info, rdata);
PageSetLSN(page, recptr);

注意:实际上是调用两次XLogInsert,除了HEAP INSERT操作的XLOG record,还有事务提交的XLOG record。

函数XLogInsert的返回值是XLogRecPtr结构类型,即LSN(log secquence number)。heap_insert函数在执行XLogInsert()后,把其返回值XLogRecPtr记录赋值给对应的page的PageHeaderData结构中,以实现WAL机制(参考PgSQL · 特性分析 · Write-Ahead Logging机制浅析)。

XLogInsert函数中会去包装一个XLOG record,并把它刷写到磁盘,我们接下来分析一下XLogInsert函数。

XLogInsert函数定义:

XLogRecPtr XLogInsert(RmgrId rmid, uint8 info, XLogRecData *rdata)

XLogInsert的三个函数参数分别是:

  • rmid

    • RmgrId类型
    • 代表本条XLOG record所属的资源管理器类型,例如我们上面的例子中INSERT操作属于RM_HEAP_ID,即堆表资源管理器
  • info

    • uint8类型
    • 代表资源管理器对应的操作,例如堆表中INSERT操作为0x00
  • rdata

    • XLogRecData指针类型(链表)
    • 每个XLogRecData结构体存储对应的资源管理器数据rmgr-specific data

之所以要用XLogRecData链,是因为在所要处理的日志记录实体数据在内存空间可能不是连续存储的,而且数据可能分布在多个缓冲区内,需要用XlogRecData链表将它们组织起来。XlogRecData数据结构如下:

typedef struct XLogRecData
{char       *data;    /*包含实体数据的起始位置*/uint32        len;        /*包含实体数据大小*/Buffer        buffer;    /*如果有buffer指明第几个缓冲区*/bool        buffer_std;    /*是否含有标准的pd_lower/pd_upper结构*/struct XLogRecData *next;    /*指向下一个XLogRecData*/
} XLogRecData;

其中,buffer_std该值为true,则容许XLOG释放备份页的空闲空间,空闲空间由pd_lower和pd_upper限定:

  • pd_lower表示页面起始位置与未分配空间开头的字节偏移
  • pd_upper表示页面末尾位置与未分配空间末尾的字节偏移

通过分析三个XLogInsert函数参数,可以看出XLogInsert主要是将rdata封装成一个XLOG record。接下来我们将分析heap_insert函数内如何对rdata进行赋值。

heap_insert函数

heap_insert函数主要操作HeapTupleData结构体,对应在每个数据页中存储的每个tuple,结构如下图所示:

tuple分为头部信息和数据信息,这里不再展开,我们将在分析PostgreSQL的MVCC机制时,将其中的结构详细分析。

heap_insert函数的主要操作如下:

  • 调用RelationGetBufferForTuple方法找到shmem里缓存数据块buffer

  • 调用RelationPutHeapTuple方法,把组装好的元组tuple放到对应buffer中合适的位置

  • 赋值XLogRecData类型变量rdata,通过代码分析可以看出rdata实际上是对tuple的内容摘要

    • XLogRecData rdata[4]; 堆表的INSERT操作有4个XLogRecData结构体组成的链表
    • rdata[0].data 存储一个xl_heap_insert结构,用于标示一些基本信息:
  1. struct xl_heap_insert
    {
    xl_heaptid target; /_ inserted tuple id /
    uint8 flags;
    /
     xl_heap_header & TUPLE DATA FOLLOWS AT END OF STRUCT _/
    } xl_heap_insert;

    
    - rdata[0].buffer = InvalidBuffer
    - rdata[1].data存储一个xl_heap_header结构,存储tuple头部的简化信息:
  2. struct xl_heap_header
    {
    uint16 t_infomask2;
    uint16 t_infomask;
    uint8 t_hoff;
    } xl_heap_header;

    - rdata[1].buffer = need_tuple_data ? InvalidBuffer : buffer;如果需要存储整个数据块,则把buffer赋值给rdata
    - rdata[2].data存储tuple头部后面的数据,比如INSERT操作的插入元组的每列的数值
    - rdata[2].buffer = need_tuple_data ? InvalidBuffer : buffer;同rdata[1]
    
  • 调用XLogInsert,将rdata封装成XLOG record写入WAL缓冲区,如果需要切换日志段文件,调用XLogWrite刷写到磁盘

经过以上分析,我们可以知道,XLOG record的核心部分是资源管理器数据(XLogRecData)和备份数据块(backup block data),这两个数据包含了我们恢复时候需要的数据。在各个资源管理器的具体操作调用XLogInsert之前,需要对这两个部分进行填充。

转载于:https://my.oschina.net/u/3949534/blog/2962193

PG中XLOG日志结构相关推荐

  1. Linux 操作系统原理 — 日志结构的文件系统与日志文件系统

    目录 文章目录 目录 日志结构的文件系统 日志文件系统 日志结构的文件系统 技术的改变会给当前的文件系统带来压力.这种情况下,CPU 会变得越来越快,磁盘会变得越来越大并且越来越便宜(但不会越来越快) ...

  2. Linux中的日志系统介绍

    一.常见的日志 日志是一个系统管理员,一个运维人员,甚至是开发人员不可或缺的东西,系统用久了偶尔也会出现一些错误,我们需要日志来给系统排错,在一些网络应用服务不能正常工作的时候,我们需要用日志来做问题 ...

  3. 大型互联网应用中的日志系统

    2019独角兽企业重金招聘Python工程师标准>>> 大型互联网应用的突出特点是应用本身规模大,结构复杂,用户访问量大.设计良好的日志系统,有助于分析流量趋势,帮助管理网络应用:有 ...

  4. Hadoop平台日志结构

    1.Hadoop集群部署 Hadoop集群: Master:10.18.18.1 Slave1:10.18.18.100 Slave2:10.18.18.101 可互相通信.可连外网,操作系统均为: ...

  5. 2.在某应用软件中需要记录业务方法的调用日志,在不修改现有业务类的基础上为每一个类提供一个日志记录代理类,在代理类中输出日志,例如在业务方法 method() 调用之前输出“方法 method() 被

    2.在某应用软件中需要记录业务方法的调用日志,在不修改现有业务类的基础上为每一个类提供一个日志记录代理类,在代理类中输出日志,例如在业务方法 method() 调用之前输出"方法 metho ...

  6. 如何使用Elasticsearch,Logstash和Kibana实时可视化Python中的日志

    by Ritvik Khanna Ritvik Khanna着 如何使用Elasticsearch,Logstash和Kibana实时可视化Python中的日志 (How to use Elastic ...

  7. hbase记录日志wal_SQL Server事务日志–第1部分–日志结构和预写日志记录(WAL)算法

    hbase记录日志wal SQL Server transaction log is one of the most critical and in the same time one of the ...

  8. 【raft】学习五:日志结构raftLog

    背景 随着对raft的学习,最近探讨了etcd/raft的日志结构,笔者今天主要分享raftLog是如何有效地保存日志. 简要 etcd/raft中Raft日志是通过raftLog结构体记录.raft ...

  9. Mars XLog日志模块集成

    学习背景: 查看官网教程,有点蒙,搜集各个大牛博客以及维基百科整理一下,在此记录方便以后查看资源整理: Mars源码下载: Github链接:(有时候网真的不行啊)https://github.com ...

最新文章

  1. mysql 表中添加唯一约束
  2. P4068-[SDOI2016]数字配对【二分,费用流】
  3. 古巴比伦乘法_古巴平台中的通用过滤器–类固醇上的excel过滤器
  4. 手机屏幕适配原理及实现
  5. ubuntu16.04安装NIVIDIA显卡驱动,cuda8.0,cuDNN6.0以及基于Anaconda安装Tensorflow-GPU
  6. javascript的概述
  7. JDK8+Ojdbc7,连接oracle
  8. 税控服务器组件接口v2.1.1.1,税控开票服务器组件接口规范标准版V1.9(2016.04.04).pdf...
  9. 人工客服行业遭重创,背后支撑的力量
  10. 读书笔记:谁都可以进外企
  11. Genlovy_Hoo大神的杰作
  12. SQL Server Always Encrypted加密使用
  13. vue3自定义指令(directive)
  14. php 汉王云名片_汉王云名片识别(SM)组件开发详解
  15. 【Android Studio】在Mac中更换JDK Location
  16. 2021.03.17 pokémon小游戏开发记录与周总结
  17. 如何获取微信开发者id及设置微信授权目录
  18. Android 安装app
  19. 使用PhoenixSuit.exe刷机失败的解决办法
  20. 如何在官网下载各个版本的tomcat

热门文章

  1. Ubuntu常用磁盘工具Disks、GParted和系统清理应用Cleaner
  2. 哲学初感 ---- 《苏菲的世界》总结
  3. Android 一体机研发之修改系统设置————声音
  4. ps滚动字教程:为图像画面添加滚动字幕
  5. python 批量处理图片
  6. 美妆科技:改变美容行业的未来
  7. 使用ssh关联github
  8. 服务器搭建网站公网ip,如何获取公网ip,自己搭建公网ip服务器的方法
  9. 天池比赛——用户情感可视化分析
  10. miui免root冻结,免root停用miui应用