• 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。

    1. void
    2. heap_redo(XLogReaderState *record)
    3. {
    4. uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
    5. /*
    6. * These operations don't overwrite MVCC data so no conflict processing is
    7. * required. The ones in heap2 rmgr do.
    8. */
    9. switch (info & XLOG_HEAP_OPMASK)
    10. {
    11. case XLOG_HEAP_INSERT:
    12. heap_xlog_insert(record);
    13. break;
    14. case XLOG_HEAP_DELETE:
    15. heap_xlog_delete(record);
    16. break;
    17. case XLOG_HEAP_UPDATE:
    18. heap_xlog_update(record, false);
    19. break;
    20. case XLOG_HEAP_TRUNCATE:
    21. /*
    22. * TRUNCATE is a no-op because the actions are already logged as
    23. * SMGR WAL records. TRUNCATE WAL record only exists for logical
    24. * decoding.
    25. */
    26. break;
    27. default:
    28. elog(PANIC, "heap_redo: unknown op code %u", info);
    29. }
    30. }

           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记录读入的缓冲区
    1. /*
    2. * XLogReadBufferForRedo
    3. * Read a page during XLOG replay
    4. */
    5. XLogRedoAction
    6. XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id,
    7. Buffer *buf)
    8. {
    9. return XLogReadBufferForRedoExtended(record, block_id, RBM_NORMAL,
    10. false, buf);
    11. }

    三、 XLogReadBufferForRedoExtended函数

    1. /*
    2. * XLogReadBufferForRedoExtended
    3. * Like XLogReadBufferForRedo, but with extra options.
    4. */
    5. XLogRedoAction
    6. XLogReadBufferForRedoExtended(XLogReaderState *record,
    7. uint8 block_id,
    8. ReadBufferMode mode, bool get_cleanup_lock,
    9. Buffer *buf)
    10. {
    11. /* If it has a full-page image and it should be restored, do it. 全页写的块 */
    12. if (XLogRecBlockImageApply(record, block_id))
    13. {
    14. Assert(XLogRecHasBlockImage(record, block_id));
    15. *buf = XLogReadBufferExtended(rnode, forknum, blkno,
    16. get_cleanup_lock ? RBM_ZERO_AND_CLEANUP_LOCK : RBM_ZERO_AND_LOCK);
    17. page = BufferGetPage(*buf);
    18. if (!RestoreBlockImage(record, block_id, page))
    19. elog(ERROR, "failed to restore block image");
    20. return BLK_RESTORED;
    21. }
    22. /* 非全页写的块 */
    23. else
    24. {
    25. *buf = XLogReadBufferExtended(rnode, forknum, blkno, mode);
    26. if (BufferIsValid(*buf))
    27. {
    28. /* 如果页面LSN >= 日志记录LSN,说明该记录已应用到页面 */
    29. if (lsn <= PageGetLSN(BufferGetPage(*buf)))
    30. return BLK_DONE;
    31. else
    32. return BLK_NEEDS_REDO;
    33. }
    34. else
    35. return BLK_NOTFOUND;
    36. }
    37. }

    参考

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

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

  • 相关阅读:
    狂神docker
    数据结构与算法(七) 二分法
    Step 3.2:垃圾收集器与内存分配策略
    获取IP地址-根据IP获取位置信息
    QC1.0、QC2.0、QC3.0、QC4.0协议介绍
    疾控物资管理系统-疾控中心物资管理系统
    IPv4子网判断
    行业洞察 | 听说,大语言模型无法接近人类水平智能?
    [HDLBits] Exams/review2015 shiftcount
    Shell速成:快速提升你的Linux命令行技能
  • 原文地址:https://blog.csdn.net/Hehuyi_In/article/details/126495178