postgresql源码学习(34)—— 事务日志⑩ - 全页写机制

67 篇文章 52 订阅
14 篇文章 4 订阅

前几天看了阿里云关于全页写的直播,打算重新再整理下关于全页写的内容。

一、 部分写/块不一致 问题

对pg来说,部分写可以发生在两种场景:

  • pg异常宕机(或者出现磁盘错误)时,数据文件中的页只写入了一部分。

  • 使用操作系统命令备份正在运行的数据库

备份途中源数据库可能被修改,此时得到的备份数据状态就是不一致的

无论是崩溃恢复还是备份还原的恢复,都无法基于不一致的数据块进行。

二、 如何解决部分写

        思路就是我额外存一份数据页的信息到WAL中,WAL中的这份数据一定是正确的(如果写入WAL失败,事务就不能提交,相当于没有执行)。

官方文档介绍如下 

发生时机:

  • 对于修改操作,当启用全页写时,pg会在每个检查点之后每个页面第一次发生变更时,将头数据和整个页面作为一条WAL记录写入WAL缓冲区。
  • 对于备份操作,强制启用全页写,只要块发生变化,就会被整块写入WAL文件(不管是不是第一次,也不管有没有检查点)。因此,它写入的量是更大的。

三、 数据恢复

  • 对于崩溃恢复,会通过checksum发现“部分写”的数据页,并将wal中保存的这个完整数据页覆盖当前损坏的数据页,然后再继续redo恢复整个数据库。
  • 对于备份恢复,在restore阶段,会直接还原不一致的块;但在recover阶段,会直接用WAL中一致的块对其进行覆盖,然后开始应用日志。

小补充:在备份恢复中,为了最后能恢复到一致的状态,全备期间除了数据文件,也需要备份期间的归档日志,用于recover阶段的应用。由于recover阶段做的是redo而不是undo,因此全备最后可以恢复到的时间点应该是备份结束时间,而不是开始时间。

四、 全页写的优缺点

安全性与性能如何均衡

优点:

  • 提高数据库的安全性,解决块不一致问题。

缺点:

  • 导致WAL文件膨胀
  • 导致额外的磁盘IO,从而影响数据库整体性能
  • 导致主备延迟变大

       一个XLOG记录长度是8字节,每个WAL段文件默认为16MB。一个WAL段可以记录将近200万事务。而如果存储8KB大小的数据块,只能储存2048个。

以下是课程直播是给的建议,可以参考

       由于阵列一般都用raid技术做了处理,所以块物理损坏的机率大大减少了;而操作系统原因导致的逻辑错误机率也比较低; 或者使用支持原子写的文件系统( ZFS),故而建议关闭full-page写。

直播课中有一个小实验,对比关闭全页写和备份期间,大DML操作产生的wal日志量

1. 实验1:非备份,full_page=off

对大表执行update,看日志切换到了几号(切换了多少个)

Update前后日志号

一共产生了43个日志

2. 实验二:备份期间,full_page=off

一共产生了134个日志,是之前的3倍以上

还可以看到错误日志中,检查点过于频繁产生了告警(每次切换就产生检查点)

五、 源码分析

1. 修改操作

       前面提到,全页写是将整个数据页写入日志记录。而从日志写入流程我们知道,这一步主要就在组装日志记录的XLogRecordAssemble函数。

会话1

vscode设断点在跟踪8632进程

会话1执行insert,模拟checkpoint后第一次块修改

首先进到了XLogInsert函数,这里我们进入XLogRecordAssemble函数

       判断是否需要备份块(全页写):优先根据flag判断,否则根据GUC参数和是否处于backup状态判断,最终根据LSN判断

通过include_image再次判断是否需要全页写

去除一些空洞

设置标记位和计数器

构造日志数据

设置FPW头信息

2. 备份阶段

pg_start_backup命令,对应函数do_pg_start_backupxlog.c文件),其中开启强制全页写:

同理在pg_stop_backup对应的函数do_pg_stop_backup,有一句关闭强制全页写:

因此手动执行pg_start_backup命令之后,备份完一定要执行pg_stop_backup,避免WAL暴增。

       另外pg_basebackup备份工具可以自动进入备份模式进行数据库备份,备份完成后自动从备份模式退出,不需执行pg_start_backup()和 pg_stop_backup()显式声明进入和退出备份模式,极大简化了原有方式,推荐使用。

这两个命令有点类似oracle的热备份操作

3. 恢复操作 —— StartupXLog函数

实际上StartupXLog函数内容很多,包括崩溃恢复、PITR、standby等。这里我们只简单看下崩溃恢复中全页写相关的内容,其他的后面再学习。

heap_redo函数会根据不同的日志记录类型,调用不同的redo函数。以insert为例,就会调用heap_xlog_insert。

        heap_xlog_insert函数会调用XLogReadBufferForRedo,再调用XLogReadBufferForRedoExtended

       FPW模式下产生的记录,在redo时直接覆盖页面。对普通日志记录,则判断该操作在故障前是否已落盘;如果已落盘,则不需要redo

XLogRedoAction
XLogReadBufferForRedoExtended(XLogReaderState *record,
                              uint8 block_id,
                              ReadBufferMode mode, bool get_cleanup_lock,
                              Buffer *buf)
{
…
    /* If it has a full-page image and it should be restored, do it. */
    if (XLogRecBlockImageApply(record, block_id))
    {
        Assert(XLogRecHasBlockImage(record, block_id));
        *buf = XLogReadBufferExtended(rnode, forknum, blkno,
                                      get_cleanup_lock ? RBM_ZERO_AND_CLEANUP_LOCK : RBM_ZERO_AND_LOCK);
        page = BufferGetPage(*buf);
   …
        return BLK_RESTORED;
    }
    else
    {
        *buf = XLogReadBufferExtended(rnode, forknum, blkno, mode);
        if (BufferIsValid(*buf))
        {
            …
             //页面LSN大于日志记录中的LSN,说明不需要再执行redo
            if (lsn <= PageGetLSN(BufferGetPage(*buf)))
                return BLK_DONE; //不需要执行redo
            else
                return BLK_NEEDS_REDO; //需要执行redo
        }
        else
            return BLK_NOTFOUND;
    }
}

参考

阿里云直播《pg-full-page机制与原理》

PostgreSQL技术内幕:事务处理深度探索》第4章

《摸着Oracle过河 ---- 大幅提升PostgreSQL性能分享 吕海波(VAGE)》

PostgreSQL中的full_page_writes的理解_weixin_33861800的博客-CSDN博客

http://mysql.taobao.org/monthly/2015/11/05/

PostgreSQL 源码解读(111)- WAL#7(Insert&WAL - XLogRecordAssemble-FPW) - 代码先锋网

pg 崩溃恢复篇(一)—— WAL的作用与全页写机制_Hehuyi_In的博客-CSDN博客_pg wal

postgresql源码学习(23)—— 事务日志④-日志组装_Hehuyi_In的博客-CSDN博客_postgresql事务日志

postgresql源码学习(33)—— 事务日志⑨ - 从insert记录看日志写入整体流程_Hehuyi_In的博客-CSDN博客

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hehuyi_In

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值