postgresql源码学习42—— 崩溃恢复④ - 日志应用

一、 redo函数

      确定日志来源之后,就可以开始应用WAL日志。在Rmgr中,每种类型的WAL日志都有startup,redo,cleanup等函数,其中最重要的就是redo函数。

       以最常见的insert为例,假如每个事务执行了插入并提交,此时数据还在buffer没有落盘,恰逢数据库宕机。在db下次启动时,就会读取到XLOG_HEAP_INSERT类型的WAL记录(对应Rmgr类型为RM_HEAP_ID),因此会进入heap_redo函数(在heapam.c文件)。

       heap_redo函数主要就是一个switch语句,根据不同类型的WAL记录,执行不同的操作。对于XLOG_HEAP_INSERT,则执行heap_xlog_insert。

void
heap_redo(XLogReaderState *record)
{
    uint8       info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;

    /*
     * These operations don't overwrite MVCC data so no conflict processing is
     * required. The ones in heap2 rmgr do.
     */

    switch (info & XLOG_HEAP_OPMASK)
    {
        case XLOG_HEAP_INSERT:
            heap_xlog_insert(record);
            break;
        case XLOG_HEAP_DELETE:
            heap_xlog_delete(record);
            break;
        case XLOG_HEAP_UPDATE:
            heap_xlog_update(record, false);
            break;
        case XLOG_HEAP_TRUNCATE:

            /*
             * TRUNCATE is a no-op because the actions are already logged as
             * SMGR WAL records.  TRUNCATE WAL record only exists for logical
             * decoding.
             */
            break;
 …
        default:
            elog(PANIC, "heap_redo: unknown op code %u", info);
    }
}	

       heap_xlog_insert函数涉及大量函数调用,目前还看不懂,暂时略过。其中跟日志应用相关的主要是XLogReadBufferForRedo函数。

二、 XLogReadBufferForRedo函数

       这个函数内容很简单,就是再调用XLogReadBufferForRedoExtended函数,但是注释很长,我们来学习学习。

1. 主要作用

       读取需要修改的页面,并根据LSN确认是否需要进行redo。如果WAL记录中包含full-page image,则需要还原该页。

2. 返回值

返回以下值之一:

  • BLK_NEEDS_REDO:该日志记录需要执行应用
  • BLK_DONE:块不需要replay
  • BLK_RESTORED:该块还原自全页写记录
  • BLK_NOTFOUND:找不到该块

3. 主要参数

  • record:WAL记录。其中的EndRecPtr用于与page LSN比较,以确定该记录是否需要执行redo(是否已经落盘)
  • block_id:在WAL记录创建时注册的块ID号
  • buf:将WAL记录读入的缓冲区
/*
 * XLogReadBufferForRedo
 *      Read a page during XLOG replay
 */

XLogRedoAction
XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id,
                      Buffer *buf)
{
    return XLogReadBufferForRedoExtended(record, block_id, RBM_NORMAL,
                                         false, buf);
}

三、 XLogReadBufferForRedoExtended函数

/*
 * XLogReadBufferForRedoExtended
 *      Like XLogReadBufferForRedo, but with extra options.
 */
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);
        if (!RestoreBlockImage(record, block_id, page))
            elog(ERROR, "failed to restore block image");
       …
        return BLK_RESTORED;
    }
/* 非全页写的块 */
    else
    {
        *buf = XLogReadBufferExtended(rnode, forknum, blkno, mode);
        if (BufferIsValid(*buf))
        {
        …
/* 如果页面LSN >= 日志记录LSN,说明该记录已应用到页面 */
            if (lsn <= PageGetLSN(BufferGetPage(*buf)))
                return BLK_DONE;
            else
                return BLK_NEEDS_REDO;
        }
        else
            return BLK_NOTFOUND;
    }
}

参考

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

https://www.bookstack.cn/read/aliyun-rds-core/393e144490c30cb5.md

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...