千家信息网

PG wal 日志的物理存储分析

发表于:2025-01-23 作者:千家信息网编辑
千家信息网最后更新 2025年01月23日,原文链接: http://www.postgres.cn/v2/news/viewone/1/385?tdsourcetag=s_pcqq_aiomsg我做人很厚道的。 必须留下链接。感谢原作者的分享
千家信息网最后更新 2025年01月23日PG wal 日志的物理存储分析


原文链接: http://www.postgres.cn/v2/news/viewone/1/385?tdsourcetag=s_pcqq_aiomsg


我做人很厚道的。 必须留下链接。


感谢原作者的分享。



PostgreSQL 事务日志WAL探秘(上篇)

原作者:何小栋(EthanHE) 创作时间:2019-01-02 09:03:46+08 采编: redraiment

发布时间: 2019-01-02 09:03:46

欢迎大家踊跃投稿,投稿信箱: press@postgres.cn

评论: 浏览: 234

摘要

事务日志是数据库的重要组成部分,存储了数据库系统中所有更改和操作的历史,以确保数据库不会因为故障(例如掉电或其他导致服务器崩溃的故障)而丢失数据。在PostgreSQL(以下简称PG)中,事务日志文件称为Write Ahead Log(以下简称WAL)。

本文对PG中事务日志文件的结构进行了简要的剖析,内容包括WAL基本术语、WAL文件组成、WAL segment file内部结构和内容剖析、XLOG Record内存组织以及 pg_waldump 工具简介。本篇是第一部分,内容包括WAL基本术语、WAL文件组成以及WAL segment file的内部结构。

一、WAL基本术语

为了更好的理解WAL和便于沟通,有必要首先对相关的WAL术语进行简要的介绍。

1、REDO log

Redo log通常称为重做日志,在写入数据文件前,每个变更都会先行写入到Redo log中。其用途和意义在于存储数据库的所有修改历史,用于数据库故障恢复(Recovery)、增量备份(Incremental Backup)、PITR(Point In Time Recovery)和复制(Replication)。

2、WAL segment file

为了便于管理,PG把事务日志文件划分为N个segment,每个segment称为WAL segment file,每个WAL segment file大小默认为16MB。

3、XLOG Record

这是一个逻辑概念,可以理解为PG中的每一个变更都对应一条XLOG Record,这些XLOG Record存储在WAL segment file中。PG读取这些XLOG Record进行故障恢复/PITR等操作。

4、WAL buffer

WA缓冲区,不管是WAL segment file的header还是XLOG Record都会先行写入到WAL缓冲区中,在"合适的时候"再通过WAL writer写入到WAL segment file中。

5、LSN

LSN即日志序列号Log Sequence Number。表示XLOG record记录写入到事务日志中位置。LSN的值为无符号64位整型(uint64)。在事务日志中,LSN单调递增且唯一。

6、checkpointer

checkpointer是PG中的一个后台进程,该进程周期性地执行checkpoint。当执行checkpoint时,该进程会把包含checkpoint信息的XLOG Record写入到当前的WAL segment file中,该XLOG Record记录包含了最新Redo pint的位置。

7、checkpoint

检查点checkpoint由checkpointer进程执行,主要的处理流程如下:

  1. 获取Redo point,构造包含此Redo point检查点(详细请参考Checkpoint结构体)信息的XLOG Record并写入到WAL segment file中;
  2. 刷新Dirty Page到磁盘上;
  3. 更新Redo point等信息到 pg_control 文件中。

8、REDO point

REDO point是PG启动恢复的起始点,是最后一次checkpoint启动时事务日志文件的末尾亦即写入Checkpoint XLOG Record时的位置(这里的位置可以理解为事务日志文件中偏移量)。

9、 pg_control

pg_control 是磁盘上的物理文件,保存检查点的基本信息,在数据库恢复中使用,可通过命令 pg_controldata 查看该文件中的内容。

二、WAL文件组成

如前所述,事务日志存储了数据库系统中所有更改和操作的历史,随着数据库的运行,事务日志大小不断的增长,那么事务日志有大小限制吗?在PG中,答案是肯定的:大小有限制。

PG使用无符号64bit整型(uint64)作为事务日志文件的寻址空间,理论上,PG的事务日志空间最大为2^64Bytes(即16EB)。这个大小有多大呢?假设某个数据库比较繁忙,每天可以产生16TB的日志文件,那么要达到事务日志文件大小的上限需要的时间是1024*1024/365天≈2800年。也就是说,虽然大小有限制,但从现阶段来看已然足够了。

显然,对于16EB的文件,OS是无法高效管理的,为此,PG把事务日志文件划分为N个大小为16M(默认值)的WAL segment file,其总体结构如下图所示:

图一 事务日志总体结构

1、WAL segment file

WAL segment file文件名称为24个字符,由3部分组成,每个部分是8个字符,每个字符是一个16进制值(即0~F)。每一部分的解析如下(在WAL segment file文件大小为16MB的情况下):

  1. 第1部分是TimeLineID,取值范围是0x00000000 -> 0xFFFFFFFF
  2. 第2部分是逻辑文件ID,取值范围是0x00000000 -> 0xFFFFFFFF
  3. 第3部分是物理文件ID,取值范围是0x00000000 -> 0x000000FF

逻辑文件ID、物理文件ID和文件大小这三部分的组合,实现了64bit的寻找空间:

  1. 逻辑文件ID是32bit的uint32(unsigned int 32bit)
  2. 物理文件ID是8bit的unit8
  3. 16M的文件大小是24bit的unit24

三者共同组成unit64(32+8+24),达到最大64bit的文件寻址空间。

2、再谈LSN

事务日志文件的LSN表示XLOG Record记录写入到事务日志文件中的位置。LSN可以理解为XLOG Record在事务日志文件中的偏移(Offset)。

LSN由3部分组成,分别是逻辑文件ID,物理文件ID和文件内偏移。如LSN:1/4288E228,其中1为逻辑文件ID,42为物理文件ID,88E228为WAL segment file文件内偏移(注:3Bytes的寻找空间为16MB)。

按此规则,给定一个LSN,很容易根据LSN号推算得到其对应的日志文件(假定时间线TimeLineID为1)。

如:LSN 1/4288E228对应的WAL segment file文件为00000001 00000001 00000042,该文件名称的前8位为时间线ID(00000001),中间8位(00000001)为逻辑文件ID,最后8位(00000042)为物理文件ID。

另外,PG也提供了相应的函数根据LSN获取日志文件名:

testdb=# SELECT pg_walfile_name('1/4288E228');     pg_walfile_name      -------------------------- 000000010000000100000042(1 row)

三、WAL segment file内部结构

WAL segment file默认大小为16MB,其内部结构如下图所示:

图二 WAL segment file内部结构

1、WAL segment file

WAL segment file内部划分为N个page(Block),每个page大小为8192 Bytes即8K,每个WAL segment file第1个page的header在PG源码中相应的数据结构是XLogLongPageHeaderData,后续其他page的header对应的数据结构是XLogPageHeaderData。在一个page中,page header之后是N个XLOG Record。

2、XLOG Record

XLOG Record由两部分组成,第一部分是XLOG Record的头部信息,大小固定(24 Bytes),对应的结构体是XLogRecord;第二部分是XLOG Record data。

XLOG Record的整体布局如下:

头部数据(固定大小的XLogRecord结构体)XLogRecordBlockHeader 结构体XLogRecordBlockHeader 结构体...XLogRecordDataHeader[Short|Long] 结构体block datablock data...main data

XLOG Record按存储的数据内容来划分,大体可以分为三类:

  1. Record for backup block:存储full-write-page的block,这种类型Record是为了解决page部分写的问题。在checkpoint完成后第一次修改数据page,在记录此变更写入事务日志文件时整页写入(需设置相应的初始化参数,默认为打开);
  2. Record for tuple data block:存储page中的tuple变更,使用这种类型的Record记录;
  3. Record for Checkpoint:在checkpoint发生时,在事务日志文件中记录checkpoint信息(其中包括Redo point)。

其中XLOG Record data是存储实际数据的地方,由以下几部分组成:

  1. 0..N个XLogRecordBlockHeader,每一个XLogRecordBlockHeader对应一个block data;
  2. XLogRecordDataHeader[Short|Long],如数据大小<256 Bytes,则使用Short格式,否则使用Long格式;
  3. block data:full-write-page data和tuple data。对于full-write-page data,如启用了压缩,则数据压缩存储,压缩后该page相关的元数据存储在XLogRecordBlockCompressHeader中;
  4. main data: /checkpoint等日志数据.

以INSERT数据为例,在插入数据时的XLOG Record data内部结构如下图所示:

图三 XLOG Record data for DML Statement

3、数据结构

1、XLogPageHeaderData结构体定义

/* * Each page of XLOG file has a header like this: * 每一个事务日志文件的page都有头部信息,结构如下: *///可作为WAL版本信息#define XLOG_PAGE_MAGIC 0xD098  /* can be used as WAL version indicator */typedef struct XLogPageHeaderData{    //WAL版本信息,PG V11.1 --> 0xD98    uint16      xlp_magic;      /* magic value for correctness checks */    //标记位(详见下面说明)    uint16      xlp_info;       /* flag bits, see below */    //page中第一个XLOG Record的TimeLineID,类型为uint32    TimeLineID  xlp_tli;        /* TimeLineID of first record on page */    //page的XLOG地址(在事务日志中的偏移),类型为uint64    XLogRecPtr  xlp_pageaddr;   /* XLOG address of this page */    /*     * When there is not enough space on current page for whole record, we     * continue on the next page.  xlp_rem_len is the number of bytes     * remaining from a previous page.     * 如果当前页的空间不足以存储整个XLOG Record,在下一个页面中存储余下的数据     * xlp_rem_len表示上一页XLOG Record剩余部分的大小     *     * Note that xl_rem_len includes backup-block data; that is, it tracks     * xl_tot_len not xl_len in the initial header.  Also note that the     * continuation data isn't necessarily aligned.     * 注意xl_rem_len包含backup-block data(full-page-write);     * 也就是说在初始的头部信息中跟踪的是xl_tot_len而不是xl_len.     * 另外要注意的是剩余的数据不需要对齐.     */    //上一页空间不够存储XLOG Record,该Record在本页继续存储占用的空间大小    uint32      xlp_rem_len;    /* total len of remaining data for record */} XLogPageHeaderData;#define SizeOfXLogShortPHD  MAXALIGN(sizeof(XLogPageHeaderData))typedef XLogPageHeaderData *XLogPageHeader;

2、XLogLongPageHeaderData结构体定义

/* * When the XLP_LONG_HEADER flag is set, we store additional fields in the * page header.  (This is ordinarily done just in the first page of an * XLOG file.)  The additional fields serve to identify the file accurately. * 如设置了XLP_LONG_HEADER标记,在page header中存储额外的字段. * (通常在每个事务日志文件也就是segment file的的第一个page中存在). * 附加字段用于准确识别文件。 */typedef struct XLogLongPageHeaderData{    //标准的头部域字段    XLogPageHeaderData std;     /* standard header fields */    //pg_control中的系统标识码    uint64      xlp_sysid;      /* system identifier from pg_control */    //交叉检查    uint32      xlp_seg_size;   /* just as a cross-check */    //交叉检查    uint32      xlp_xlog_blcksz;    /* just as a cross-check */} XLogLongPageHeaderData;#define SizeOfXLogLongPHD   MAXALIGN(sizeof(XLogLongPageHeaderData))//指针typedef XLogLongPageHeaderData *XLogLongPageHeader;/* When record crosses page boundary, set this flag in new page's header *///如果XLOG Record跨越page边界,在新page header中设置该标志位#define XLP_FIRST_IS_CONTRECORD     0x0001//该标志位标明是"long"页头/* This flag indicates a "long" page header */#define XLP_LONG_HEADER             0x0002/* This flag indicates backup blocks starting in this page are optional *///该标志位标明从该页起始的backup blocks是可选的(不一定存在)#define XLP_BKP_REMOVABLE           0x0004//xlp_info中所有定义的标志位(用于page header的有效性检查)/* All defined flag bits in xlp_info (used for validity checking of header) */#define XLP_ALL_FLAGS               0x0007#define XLogPageHeaderSize(hdr)     \    (((hdr)->xlp_info & XLP_LONG_HEADER) ? SizeOfXLogLongPHD : SizeOfXLogShortPHD)

3、XLogRecord结构体定义

/* * The overall layout of an XLOG record is: *      Fixed-size header (XLogRecord struct) *      XLogRecordBlockHeader struct *      XLogRecordBlockHeader struct *      ... *      XLogRecordDataHeader[Short|Long] struct *      block data *      block data *      ... *      main data * XLOG record的整体布局如下: *         固定大小的头部(XLogRecord 结构体) *        XLogRecordBlockHeader 结构体 *        XLogRecordBlockHeader 结构体 *        ... *        XLogRecordDataHeader[Short|Long] 结构体 *        block data *        block data *        ... *        main data * * There can be zero or more XLogRecordBlockHeaders, and 0 or more bytes of * rmgr-specific data not associated with a block.  XLogRecord structs * always start on MAXALIGN boundaries in the WAL files, but the rest of * the fields are not aligned. * 其中,XLogRecordBlockHeaders可能有0或者多个,与block无关的0或多个字节的rmgr-specific数据 * XLogRecord通常在WAL文件的MAXALIGN边界起写入,但后续的字段并没有对齐 * * The XLogRecordBlockHeader, XLogRecordDataHeaderShort and * XLogRecordDataHeaderLong structs all begin with a single 'id' byte. It's * used to distinguish between block references, and the main data structs. * XLogRecordBlockHeader/XLogRecordDataHeaderShort/XLogRecordDataHeaderLong开头是占用1个字节的"id". * 用于区分block依赖和main data结构体. */typedef struct XLogRecord{    //record的大小    uint32      xl_tot_len;     /* total len of entire record */    //xact id    TransactionId xl_xid;       /* xact id */    //指向log中的前一条记录    XLogRecPtr  xl_prev;        /* ptr to previous record in log */    //标识位,详见下面的说明    uint8       xl_info;        /* flag bits, see below */    //该记录的资源管理器    RmgrId      xl_rmid;        /* resource manager for this record */    /* 2 bytes of padding here, initialize to zero */    //2个字节的crc校验位,初始化为0    pg_crc32c   xl_crc;         /* CRC for this record */    /* XLogRecordBlockHeaders and XLogRecordDataHeader follow, no padding */    //接下来是XLogRecordBlockHeaders和XLogRecordDataHeader} XLogRecord;//宏定义:XLogRecord大小#define SizeOfXLogRecord    (offsetof(XLogRecord, xl_crc) + sizeof(pg_crc32c))/* * The high 4 bits in xl_info may be used freely by rmgr. The * XLR_SPECIAL_REL_UPDATE and XLR_CHECK_CONSISTENCY bits can be passed by * XLogInsert caller. The rest are set internally by XLogInsert. * xl_info的高4位由rmgr自由使用. * XLR_SPECIAL_REL_UPDATE和XLR_CHECK_CONSISTENCY由XLogInsert函数的调用者传入. * 其余由XLogInsert内部使用. */#define XLR_INFO_MASK           0x0F#define XLR_RMGR_INFO_MASK      0xF0/* * If a WAL record modifies any relation files, in ways not covered by the * usual block references, this flag is set. This is not used for anything * by PostgreSQL itself, but it allows external tools that read WAL and keep * track of modified blocks to recognize such special record types. * 如果WAL记录使用特殊的方式(不涉及通常块引用)更新了关系的存储文件,设置此标记. * PostgreSQL本身并不使用这种方法,但它允许外部工具读取WAL并跟踪修改后的块, *   以识别这种特殊的记录类型。 */#define XLR_SPECIAL_REL_UPDATE  0x01/* * Enforces consistency checks of replayed WAL at recovery. If enabled, * each record will log a full-page write for each block modified by the * record and will reuse it afterwards for consistency checks. The caller * of XLogInsert can use this value if necessary, but if * wal_consistency_checking is enabled for a rmgr this is set unconditionally. * 在恢复时强制执行一致性检查. * 如启用此功能,每个记录将为记录修改的每个块记录一个完整的页面写操作,并在以后重用它进行一致性检查。 * 在需要时,XLogInsert的调用者可使用此标记,但如果rmgr启用了wal_consistency_checking, *   则会无条件执行一致性检查. */#define XLR_CHECK_CONSISTENCY   0x02

4、XLogRecordBlockHeader结构体定义

/* * Header info for block data appended to an XLOG record. * 追加到XLOG record中block data的头部信息 * * 'data_length' is the length of the rmgr-specific payload data associated * with this block. It does not include the possible full page image, nor * XLogRecordBlockHeader struct itself. * 'data_length'是与此块关联的rmgr特定payload data的长度。 * 它不包括可能的full page image,也不包括XLogRecordBlockHeader结构体本身。 * * Note that we don't attempt to align the XLogRecordBlockHeader struct! * So, the struct must be copied to aligned local storage before use. * 注意:我们不打算尝试对齐XLogRecordBlockHeader结构体! * 因此,在使用前,XLogRecordBlockHeader必须拷贝到对齐的本地存储中. */typedef struct XLogRecordBlockHeader{    //块引用ID    uint8       id;             /* block reference ID */    //在关系中使用的fork和flags    uint8       fork_flags;     /* fork within the relation, and flags */    //payload字节大小    uint16      data_length;    /* number of payload bytes (not including page                                 * image) */    /* If BKPBLOCK_HAS_IMAGE, an XLogRecordBlockImageHeader struct follows *//* If BKPBLOCK_SAME_REL is not set, a RelFileNode follows *//* BlockNumber follows */    //如BKPBLOCK_HAS_IMAGE,后续为XLogRecordBlockImageHeader结构体        //如BKPBLOCK_SAME_REL没有设置,则为RelFileNode    //后续为BlockNumber} XLogRecordBlockHeader;#define SizeOfXLogRecordBlockHeader (offsetof(XLogRecordBlockHeader, data_length) + sizeof(uint16))

5、XLogRecordDataHeader[Short|Long]结构体定义

/* * XLogRecordDataHeaderShort/Long are used for the "main data" portion of * the record. If the length of the data is less than 256 bytes, the short * form is used, with a single byte to hold the length. Otherwise the long * form is used. * XLogRecordDataHeaderShort/Long用于记录的"main data"部分。 * 如果数据的长度小于256字节,则使用短格式,用一个字节保存长度。 * 否则使用长形式。 * * (These structs are currently not used in the code, they are here just for * documentation purposes). * (这些结构体不会再代码中使用,在这里是为了文档记录的目的) */typedef struct XLogRecordDataHeaderShort{    uint8       id;             /* XLR_BLOCK_ID_DATA_SHORT */    uint8       data_length;    /* number of payload bytes */}           XLogRecordDataHeaderShort;#define SizeOfXLogRecordDataHeaderShort (sizeof(uint8) * 2)typedef struct XLogRecordDataHeaderLong{    uint8       id;             /* XLR_BLOCK_ID_DATA_LONG */    /* followed by uint32 data_length, unaligned */    //接下来是无符号32位整型的data_length(未对齐)}           XLogRecordDataHeaderLong;#define SizeOfXLogRecordDataHeaderLong (sizeof(uint8) + sizeof(uint32))/* * Block IDs used to distinguish different kinds of record fragments. Block * references are numbered from 0 to XLR_MAX_BLOCK_ID. A rmgr is free to use * any ID number in that range (although you should stick to small numbers, * because the WAL machinery is optimized for that case). A couple of ID * numbers are reserved to denote the "main" data portion of the record. * 块id用于区分不同类型的记录片段。 * 块引用编号从0到XLR_MAX_BLOCK_ID。 * rmgr可以自由使用该范围内的任何ID号 *   (尽管您应该坚持使用较小的数字,因为WAL机制针对这种情况进行了优化)。 * 保留两个ID号来表示记录的"main"数据部分。 * * The maximum is currently set at 32, quite arbitrarily. Most records only * need a handful of block references, but there are a few exceptions that * need more. * 目前的最大值是32,非常随意。 * 大多数记录只需要少数块引用,但也有少数的例外,需要更多。 */#define XLR_MAX_BLOCK_ID            32#define XLR_BLOCK_ID_DATA_SHORT     255#define XLR_BLOCK_ID_DATA_LONG      254#define XLR_BLOCK_ID_ORIGIN         253#endif                          /* XLOGRECORD_H */

6、 xl_heap_header 结构体定义

/* * We don't store the whole fixed part (HeapTupleHeaderData) of an inserted * or updated tuple in WAL; we can save a few bytes by reconstructing the * fields that are available elsewhere in the WAL record, or perhaps just * plain needn't be reconstructed.  These are the fields we must store. * NOTE: t_hoff could be recomputed, but we may as well store it because * it will come for free due to alignment considerations. * PG不会在WAL中存储插入/更新的元组的全部固定部分(HeapTupleHeaderData); *   我们可以通过重新构造在WAL记录中可用的一些字段来节省一些空间,或者直接扁平化处理。 * 这些都是我们必须存储的字段。 * 注意:t_hoff可以重新计算,但我们也需要存储它,因为出于对齐的考虑,会被析构。 */typedef struct xl_heap_header{    uint16      t_infomask2;//t_infomask2标记    uint16      t_infomask;//t_infomask标记    uint8       t_hoff;//t_hoff} xl_heap_header;//HeapHeader的大小#define SizeOfHeapHeader    (offsetof(xl_heap_header, t_hoff) + sizeof(uint8))7)    xl_heap_insert结构体定义/* * xl_heap_insert/xl_heap_multi_insert flag values, 8 bits are available. *//* PD_ALL_VISIBLE was cleared */#define XLH_INSERT_ALL_VISIBLE_CLEARED          (1<<0)#define XLH_INSERT_LAST_IN_MULTI                (1<<1)#define XLH_INSERT_IS_SPECULATIVE               (1<<2)#define XLH_INSERT_CONTAINS_NEW_TUPLE           (1<<3)/* This is what we need to know about insert *///这是在插入时需要获知的信息typedef struct xl_heap_insert{    //已成功插入的元组的偏移    OffsetNumber offnum;        /* inserted tuple's offset */    uint8       flags;            //标记    /* xl_heap_header & TUPLE DATA in backup block 0 */    //xl_heap_header & TUPLE DATA在备份块0中} xl_heap_insert;//xl_heap_insert大小#define SizeOfHeapInsert    (offsetof(xl_heap_insert, flags) + sizeof(uint8))

四、参考资料

  1. Write Ahead Logging - WAL: http://www.interdb.jp/pg/pgsql09.html
  2. PG Source Code: https://doxygen.postgresql.org
  3. WAL Internals Of PostgreSQL: https://www.pgcon.org/2012/schedule/attachments/258_212_Internals%20Of%20PostgreSQL%20Wal.pdf
  4. 关于结构体占用空间大小总结: https://blog.csdn.net/Netown_Ethereal/article/details/38898003
  5. PG 11 Document: https://www.postgresql.org/docs/11/pgwaldump.html
文件 结构 日志 数据 事务 大小 存储 部分 信息 空间 数据库 检查 物理 头部 标记 逻辑 字段 字节 类型 偏移 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 深圳双喜互联网络科技有限公司 网络安全宣传员手抄报一等奖 广电网络技术主管工作总结 画服务器架构图 数据库与网络工程师哪个好 云数据库哪个牌子安全 天然产物化学数据库 阿左旗网络安全应急指挥中心 陈彬数据库技术项目导入脚本 超算服务器哪个品牌好 福州继教网络安全 保护未成年的网络安全 苏州培训软件开发机构 北京网络安全日答题 人社系统网络安全知识知多少 国产服务器是x86吗 衡水伟创网络技术 李平 网络安全模式下能装打印机驱动吗 东城区信息网络技术服务优点 陕西智慧社区软件开发电话 计算机网络技术平均工资是多少 河南计算机网络技术升本成功 计算机网络与数据库选谁 4399服务器账号查询 时间与服务器同步 郑州linux软件开发 陈彬数据库技术项目导入脚本 做一个服务器要用哪些 北京网络安全日答题 软件开发是正规的吗
0