温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

InnoDB Redo Log的设计原理以及源码是怎样的

发布时间:2021-10-08 17:23:31 来源:亿速云 阅读:275 作者:柒染 栏目:MySQL数据库

本篇文章为大家展示了InnoDB Redo Log的设计原理以及源码是怎样的,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

这篇文章主要学习 InnoDB Redo Log 的流程。Redo Log 是 InnoDB 实现数据一致性和持久化存储的关键,主要从设计原理和部分源码实现出发,对其中的知识点进行归纳总结。

Redo Log Buffer & Redo Log File

前面两篇文章已经描述过,InnoDB 使用 Redo Log 来保证数据的一致性和可持久性,它采用 WAL 机制,即先写日志再写数据。具体来说,InnoDB 进行写操作时,先将数据操作记录在 log buffer 中,然后将 log buffer 中的数据刷到磁盘 log file 中,后续数据再落到数据 ibd 文件这一步骤由 checkpoint 来保证。其数据流如下图所示。

InnoDB Redo Log的设计原理以及源码是怎样的

另外有一个细节值得注意:虽然对于一个事务来讲,还需要再成功写入 binlog 文件,才能认为事务完成,但单从存储层的视角来看,在某个时刻,数据文件加上 Redo Log File 就是此时刻数据库的一个完整快照,像 Xtrabackup 这类的物理备份工具,就是通过拷贝数据文件和 Redo Log File 来进行全量备份的。

Redo Log 元数据及其初始化流程

InnoDB 使用 log_sys 这个对象来管理 Redo Log Buffer,其结构体为 log_t,它在源码中的定义(部分数据被折叠)如下。

InnoDB Redo Log的设计原理以及源码是怎样的log_t 结构体定义 (/innobase/include/log0log.h)

log_sys 主要包括以下元数据信息:

InnoDB Redo Log的设计原理以及源码是怎样的

在 log_sys 元数据信息中有一个非常重要的概念 — LSN。

LSN 即日志序列号( Log Sequence Number ),它代表 Redo Log 的序号,它是单调递增的,每写入一个 Redo Log 时,LSN 就会递增该 Redo Log 写入的字节数,因此,LSN 就像是时间点一样,记录了每个 Redo Log 产生的时序,并且和 Redo Log 一一对应,我们后面会具体描述 LSN 和 Redo Log 之间是如何转换的。

log_sys 对象是在 InnoDB 启动时,由 log_init() 函数负责初始化,主要是对元数据信息进行赋值操作。其部分代码实现如下:

InnoDB Redo Log的设计原理以及源码是怎样的

log_init()函数实现 (/innobase/log/log0log.cc)

log_init() 函数另一个重要的步骤是对 log 对象 log_block 进行初始化。log_block 是 Redo Log 的最小数据管理单元,其大小为 512 Bytes。log_block 并没有单独的结构体来进行管理,其元数据信息存在于自身的前 12 个字节中,这部分信息称为 log_block_header,另外,log_block 还包括 8 个字节的 log_block_tail 信息,因此,每个 log_block 实际可存储空间为 492 Bytes。log_sys 和 log_block 的结构信息如下图。

InnoDB Redo Log的设计原理以及源码是怎样的

正如上图所标注的信息,log_block_header 包含如下信息:

InnoDB Redo Log的设计原理以及源码是怎样的

log_block_init() 函数进行的操作如下:

InnoDB Redo Log的设计原理以及源码是怎样的

log_block_init()函数实现 (/innobase/include/log0log.ic)

首先,通过该 log_block 的 lsn 值计算出 log_block 的编号,即 log_block_hdr_no,前面已经提到过,log_block 没有其他的数据结构进行组织,因此该编号唯一标识了 log_block 在 log buffer 中的位置。lsn 到 log_block_hdr_no 的转换由函数 log_block_convert_lsn_to_no() 实现,实现逻辑为:

UNIV_INLINE
ulint
log_block_convert_lsn_to_no(
/*========================*/
    lsn_t    lsn)    /*!< in: lsn of a byte within the block */
{
    return(((ulint) (lsn / OS_FILE_LOG_BLOCK_SIZE) & 0x3FFFFFFFUL) + 1);
}

这个逻辑还是比较简单,因为日志都是按 log_block_size(512 Bytes) 存储的,并且 lsn 单调增加,因此 lsn 其实是 log_block_size 的倍数再加上当前偏移量。

计算出log_block编号后,log_block_set_hdr_no() 函数将该编号记录在 log_block 的前 4 个字节中。然后 log_block_set_data_len() 函数设置该 log_block 默认已使用的空间 LOG_BLOCK_HDR_SIZE,即 12 Bytes。

在实际记录 log 的过程中,一次写 log 的大小可能大于 512 Bytes,即连续占用多个 log_block,所以 log_block 中可能包含多个 log 的内容,因此用 log_block_first_rec_group 记录了 log_block 中第一个日志的偏移量。该变量的值也会被设置成 LOG_BLOCK_HDR_SIZE 的大小。

上面讲到的 log_init() 的整个流程是在 InnoDB 存储引擎的初始化过程中实现的。学习一个复杂系统,从它的初始化流程入手会比较有帮助,就像学习一个文件系统,从 xxxfs_init() 入手,你可以很快的了解 super_block 是如何初始化的,inodes 是如何组织的,学习 InnoDB 也是同样的道理。InnoDB 的初始化流程是由 innobase_start_or_create_for_mysql() 这个函数来实现的,它负责初始化 InnoDB 所需要的各种元数据信息,如 Buffer Pool 结构、LRU 链表、各种文件数据结构、日志数据结构、锁信息、设置 InnoDB 的各种参数变量等。除了我们讲到的 log_init() 流程,还包括 fil_init(), buf_pool_init(), fsp_init(), buf_flush_page_cleaner_init() 等。例如 buf_pool_init() 流程所做的工作初始化 Buffer Pool Instance 对象的结构体 buf_pool_t,包括对 buffer_pool_chunks 的初始化,多个 LRU 链表和指针变量的初始化等。

CheckPoint & LSN

前面已经提到,Buffer Pool 中的数据落盘操作,是由 checkpoint 来执行的。checkpoint 负责将内存数据同步到磁盘数据文件中,以确保数据的一致性。checkpoint 执行的情况决定了数据库在进行灾难恢复时所需要的时间,在数据库进行灾难恢复时,只需要恢复从 checkpoint 到最近的 Redo Log 这段日志中的数据,就可以恢复到宕机之前的状态。LSN 记录了 Redo Log 产生的时序,日志写到 log buffer 以及 log buffer 数据落盘到 log file,都是根据 LSN 来往前推进的。而 checkpoint 也是根据 LSN 的时序来进行刷盘操作的。在 MySQL 中执行 show engine innodb status 可以看到:

InnoDB Redo Log的设计原理以及源码是怎样的

由于系统写入量较少,当前系统产生的 lsn 和已经刷到 Redo Log File 的 lsn 值是一致的。而 Buffer Pool 中数据页刷盘的进度和执行 checkpoint 的进度都较为落后。

Group Commit

InnoDB 允许多个事务产生的 Redo Log 一起提交,来减少磁盘 I/O。首先我们再来阐述一下 Redo Log 和 Binlog 的区别。Redo Log 是 InnoDB 存储层特有的日志,记录的是物理页数据的变更,而 Binlog 是 MySQL 都有的,记录的逻辑日志。Binlog 只有事务提交时进行一次写入,而 Redo Log 的写入时机则有多种。首先,在事务提交时,若设置了参数 innodb_flush_log_at_trx_commit 为 1,则会进行刷盘操作;其次,当 Redo Log Buffer 空间不足时,系统也会强制进行刷盘操作;另外后台线程也会每秒进行一次刷盘操作。

如果是在多并发事务的情况下,某个事务在事务过程中产生的 log (已写入到 log buffer 中),可能会被其他事务的提交带到 log file 中,也可能被后台线程进行刷盘,这样就达到了 Group Commit 的效果。同时也可以看到,一个事务产生的 log 并不是在事务提交时一次写到 log file 的,而是在事务过程中不断写入到 log file 的。

上述内容就是InnoDB Redo Log的设计原理以及源码是怎样的,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI