PostgreSQL的full_page_writes

2024-01-25 20:04
文章标签 postgresql page full writes

本文主要是介绍PostgreSQL的full_page_writes,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概念介绍

页断裂

页断裂也可以称为页折断或者半页写。PostgreSQL中,一个page默认为8kb,数据的写入是以page为单位的。而操作系统的一个page往往是4kb或者更小,这将导致PostgreSQL在写一个page到磁盘时,操作系统可能会将PG的一个page,分两次写入到磁盘。 如果系统出现故障,则会出现PG的一个page,操作系统只写了一半到磁盘上,这种现象称之为页折断。

page=8kb
page=4kb
PostgreSQL
OS
Disk

当出现页折断后,页的操作可能只完成了一部分,导致磁盘上的页同时存在新旧数据,这个时候,仅通过wal的数据更改记录,并不足以恢复该页面。

目前市面上的数据库,解决页折断问题,一般有两种方法,一种是full_page_writes,一种是double write。

采用full_page_writes的数据库:

  • PostgreSQL

    采用double write的数据库:

  • openGauss

    • 需结合增量检查点使用;
  • MySQL

full_page_writes

full_page_write是PostgreSQL的GUC参数,如果启用了此参数,在PG执行了checkpoint后,会将Buffer Poll中首次修改的page,整个page连同DML修改语句,都存储到wal日志中。

PostgreSQL官网对其解释如下:
full_page_writes (boolean)
When this parameter is on, the PostgreSQL server writes the entire content of each disk page to WAL during the first modification of that page after a checkpoint. This is needed because a page write that is in process during an operating system crash might be only partially completed, leading to an on-disk page that contains a mix of old and new data. The row-level change data normally stored in WAL will not be enough to completely restore such a page during post-crash recovery. Storing the full page image guarantees that the page can be correctly restored, but at the price of increasing the amount of data that must be written to WAL. (Because WAL replay always starts from a checkpoint, it is sufficient to do this during the first change of each page after a checkpoint. Therefore, one way to reduce the cost of full-page writes is to increase the checkpoint interval parameters.)

Turning this parameter off speeds normal operation, but might lead to either unrecoverable data corruption, or silent data corruption, after a system failure. The risks are similar to turning off fsync, though smaller, and it should be turned off only based on the same circumstances recommended for that parameter.

Turning off this parameter does not affect use of WAL archiving for point-in-time recovery (PITR) (see Section 25.3).

This parameter can only be set in the postgresql.conf file or on the server command line. The default is on.

关键数据结构

XLogRecordBlockImageHeader

记录full-page image的相关信息,该image是否被压缩、在redo过程中,是否应该恢复整个image等信息。

typedef struct XLogRecordBlockImageHeader
{uint16        length;         /* number of page image bytes */uint16        hole_offset;    /* number of bytes before "hole" */uint8        bimg_info;      /* flag bits, see below */
} XLogRecordBlockImageHeader;

其中,bimg_info用于标识image的信息,其有如下取值:

#define BKPIMAGE_HAS_HOLE        0x01        /* page image has "hole" */
#define BKPIMAGE_IS_COMPRESSED        0x02    /* page image is compressed: image是否被压缩 */
#define BKPIMAGE_APPLY        0x04            /* page image should be restored during replay: 重放时,是否需要恢复整个image */

XLogRedoAction

枚举类型,XLogReadBufferForRedo函数的返回值为XLogRedoAction类型;

typedef enum
{BLK_NEEDS_REDO,     /* changes from WAL record need to be applied */BLK_DONE,           /* block is already up-to-date */BLK_RESTORED,       /* block was restored from a full-page image */BLK_NOTFOUND        /* block was not found (and hence does not need to be replayed) */
} XLogRedoAction;

DecodedBkpBlock

记录page相关的信息,该信息从xlog的record中解析出来,在redo该条record时,将根据has_image、apply_image等信息,决定是否恢复整个page;

typedef struct
{/* Is this block ref in use? */bool        in_use;/* Identify the block this refers to */RelFileNode rnode;ForkNumber    forknum;BlockNumber blkno;/* copy of the fork_flags field from the XLogRecordBlockHeader */uint8        flags;/* Information on full-page image, if any */bool        has_image;    /* has image, even for consistency checking */bool        apply_image;  /* has image that should be restored */char       *bkp_image;uint16        hole_offset;uint16        hole_length;uint16        bimg_len;uint8        bimg_info;/* Buffer holding the rmgr-specific data associated with this block */bool        has_data;char       *data;uint16        data_len;uint16        data_bufsz;
} DecodedBkpBlock;

full page的写入和恢复流程

数据块的写出和载入

在PostgreSQL数据库中,用户访问或者修改元组时,数据块的载入和写出的层次结构,如下所示:

读/写元组操作
写出
载入
写出
载入
写出
载入
写出
载入
元组访问
共享缓冲池
存储管理器
磁盘管理器
虚拟文件管理
物理文件

整页写入

当PostgreSQL修改了内存块,调用XlogInsert接口,记录wal日志时,在XlogRecordAssemble函数中,会判断该page是否为checkpoint后的首次修改,如果是首次修改,则将该page存储到wal文件中。

关键代码如下:

static XLogRecData *
XLogRecordAssemble(RmgrId rmid, uint8 info, XLogRecPtr RedoRecPtr, bool doPageWrites, XLogRecPtr *fpw_lsn)
{.../* Determine if this block needs to be backed up */if (regbuf->flags & REGBUF_FORCE_IMAGE)needs_backup = true;else if (regbuf->flags & REGBUF_NO_IMAGE)needs_backup = false;else if (!doPageWrites)needs_backup = false;else{/** We assume page LSN is first data on *every* page that can be* passed to XLogInsert, whether it has the standard page layout* or not.*/XLogRecPtr    page_lsn = PageGetLSN(regbuf->page);    //通过PageGetLSN函数,获取该page的LSNneeds_backup = (page_lsn <= RedoRecPtr);             // 将page的LSN,与上次checkpoint的redo点进行比较,如果该page是首次修改,LSN应该小于等于RedoRecPtrif (!needs_backup){if (*fpw_lsn == InvalidXLogRecPtr || page_lsn < *fpw_lsn)*fpw_lsn = page_lsn;}}...
}
首次修改
非首次修改
PostgreSQL修改了Buffer-Pool中的page
调用XlogInsert记录wal日志
调用XlogRecordAssemble组装record
判断该page是否为checkpoint后的首次修改?
记录整个page到wal文件中
仅记录修改该page的DML语句

整页恢复

从record中,解析出has_image、apply_image:

  1. 当PostgreSQL数据库异常关闭后,再重新启动PG,startup进程进入到故障恢复流程;
  2. 根据pg_control记录的checkpoint信息,结合xlog日志,获取redo点;
  3. 循环从redo点读取wal记录;
  4. 解析wal记录中,获取DecodedBkpBlock结构;
  5. 根据DecodedBkpBlock中的has_image、apply_image等参数,决定是否从WAL中读取image,恢复数据,使PostgreSQL重新达到数据一致性。

rmgr根据资源类型,调用不同的redo函数,重做wal日志:

  • 在redo的时候,资源管理器会根据资源的类型,调用该资源对应的redo函数进行重做;
  • 下面的流程,仅选取heap_redo做流程分析。
StartupXLOG
读取并解析wal日志
ReadRecord
XlogReadRecord
DecodeXLogRecord
从record中解析出has_image\apply_image等
重做读取的wal日志
rm_redo
xlog_redo
xact_redo
smgr_redo
relmap_redo
standby_redo
heap2_redo
heap_redo
btree_redo
hash_redo
gin_redo
gist_redo
...
heap_log_insert
XLogReadBufferForRedo
XLogReadBufferForRedoExtended
XlogRecBlockImageApply
XlogRecHasBlockImage
RestoreBlockImage

部分函数功能说明:

  bool DecodeXLogRecord(XLogReaderState *state, XLogRecord *record, char **errormsg)功能:从record中,解析出has_image、apply_image等信息;bool RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)功能:从record中,解析出full-page image;#define XLogRecHasBlockImage(decoder, block_id) \    ((decoder)->blocks[block_id].has_image)功能:判断是否存储了image;#define XLogRecBlockImageApply(decoder, block_id) \    ((decoder)->blocks[block_id].apply_image)功能:判断是否需要重放该image;

小结

  • linux下,可使用getconf PAGE_SIZE指令,查看系统的块大小;
  • PostgreSQL的块大小,在编译pg时,可以通过–with-blocksize 参数修改;
  • 启用full_page_writes参数,必然带来性能的损耗,是否启用full_page_writes,需要看存储设备是否能避免半页写的问题,以及是否有其他手段恢复坏块等;

这篇关于PostgreSQL的full_page_writes的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/644444

相关文章

MySQL报错sql_mode=only_full_group_by的问题解决

《MySQL报错sql_mode=only_full_group_by的问题解决》本文主要介绍了MySQL报错sql_mode=only_full_group_by的问题解决,文中通过示例代码介绍的非... 目录报错信息DataGrip 报错还原Navicat 报错还原报错原因解决方案查看当前 sql mo

对postgresql日期和时间的比较

《对postgresql日期和时间的比较》文章介绍了在数据库中处理日期和时间类型时的一些注意事项,包括如何将字符串转换为日期或时间类型,以及在比较时自动转换的情况,作者建议在使用数据库时,根据具体情况... 目录PostgreSQL日期和时间比较DB里保存到时分秒,需要和年月日比较db里存储date或者ti

PostgreSQL如何查询表结构和索引信息

《PostgreSQL如何查询表结构和索引信息》文章介绍了在PostgreSQL中查询表结构和索引信息的几种方法,包括使用`d`元命令、系统数据字典查询以及使用可视化工具DBeaver... 目录前言使用\d元命令查看表字段信息和索引信息通过系统数据字典查询表结构通过系统数据字典查询索引信息查询所有的表名可

PostgreSQL如何用psql运行SQL文件

《PostgreSQL如何用psql运行SQL文件》文章介绍了两种运行预写好的SQL文件的方式:首先连接数据库后执行,或者直接通过psql命令执行,需要注意的是,文件路径在Linux系统中应使用斜杠/... 目录PostgreSQ编程L用psql运行SQL文件方式一方式二总结PostgreSQL用psql运

PostgreSQL核心功能特性与使用领域及场景分析

PostgreSQL有什么优点? 开源和免费 PostgreSQL是一个开源的数据库管理系统,可以免费使用和修改。这降低了企业的成本,并为开发者提供了一个活跃的社区和丰富的资源。 高度兼容 PostgreSQL支持多种操作系统(如Linux、Windows、macOS等)和编程语言(如C、C++、Java、Python、Ruby等),并提供了多种接口(如JDBC、ODBC、ADO.NET等

PostgreSQL中的多版本并发控制(MVCC)深入解析

引言 PostgreSQL作为一款强大的开源关系数据库管理系统,以其高性能、高可靠性和丰富的功能特性而广受欢迎。在并发控制方面,PostgreSQL采用了多版本并发控制(MVCC)机制,该机制为数据库提供了高效的数据访问和更新能力,同时保证了数据的一致性和隔离性。本文将深入解析PostgreSQL中的MVCC功能,探讨其工作原理、使用场景,并通过具体SQL示例来展示其在实际应用中的表现。 一、

PostgreSQL入门介绍

一、PostgreSQL 背景及主要功能介绍 1、背景 PG数据库,全称为PostgreSQL数据库,是一款开源的关系型数据库管理系统(RDBMS)。其起源可以追溯到20世纪80年代末和90年代初,由加拿大的计算机科学家Michael Stonebraker及其团队在加州大学伯克利分校启动。该项目旨在创建一个强大的、开源的关系型数据库管理系统,作为早期关系型数据库系统Ingres的继承者。Mi

vue中路由管理(vue-router,page)使用总结

现在的项目都以模块化的方式去开发,所以在这样的开发模式下,如何更好的去管理路由是开发中所需要考虑的重点,幸运的是当前的开发中已经有了成熟的中间件去管理,我们只需要用就可以了 下面是我在学习vue-router的时候在原来基础上修改出来的demo,也是为了有助于对vue-router的理解 首先理解下vue官网的一个示例demo https://jsfiddle.net/yyx990803/x

PostgreSQL索引介绍

梦中彩虹   博客园首页新随笔联系管理 随笔 - 131  文章 - 1  评论 - 14 PostgreSQL索引介绍 INDEX 索引是增强数据库性能的常用方法。索引使得数据库在查找和检索数据库的特定行的时候比没有索引快的多。但索引也增加了整个数据库系统的开销,所以应该合理使用。 介绍 假设我们有一个类似这样的表: CREATE TABLE test1 (id integ

PostgreSQL分区表(partitioning)应用实例详解

https://www.jb51.net/article/97937.htm   PostgreSQL分区表(partitioning)应用实例详解  更新时间:2016年11月22日 10:25:58   作者:小灯光环    我要评论   这篇文章主要为大家详细介绍了PostgreSQL分区表(partitioning)应用实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下