redolog

redolog(重做日志)是InnoDB存储引擎独有的,它让MySQL拥有了崩溃恢复能力。

它记录了数据页上的改动。事务中修改了的数据,将会备份存储。MySQL实例挂了或宕机了,重启时,InnoDB存储引擎会使用redo log恢复数据,保证数据的持久性与完整性。

PS:每条 redo 记录由“表空间号+数据页号+偏移量+修改数据长度+具体修改的数据”组成

为什么不直接刷数据页到磁盘呢?

数据页大小是16KB,刷盘比较耗时。数据页刷盘是随机写的操作,因为一个数据页对应的位置可能在硬盘文件的随机位置,所以性能是很差。

所有有了redolog,写 redolog,一行记录可能就占几十 Byte,只包含表空间号、数据页号、磁盘文件偏移量、更新值,再加上是顺序写,所以刷盘速度很快。性能会远远超过刷数据页的方式,这也让数据库的并发能力更强。

redolog的作用

  • redo log主要用于MySQL异常重启后的一种数据恢复手段,确保了数据的一致性。
  • 为了配合MySQL的WAL技术(预写日志)机制。因为MySQL进行更新操作,为了能够快速响应,所以采用了异步写回磁盘的技术,写入内存后就返回。但是这样,会存在crash后内存数据丢失的隐患,而redo log具备crash safe的能力。

redolog的写入方式

redo log包括两部分内容,分别是内存中的日志缓冲(redo log buffer)和磁盘上的日志文件(redo log file)。详见:Buffer Pool

先写入redo log buffer,后续再写到redo log file。这种先写内存,再写磁盘的技术,就是WAL。对应了事务的两阶段提交

在计算机操作系统中,用户空间(user space)下的缓冲区数据,一般是无法直接写入磁盘的,必须经过操作系统内核空间缓冲区(即OS Buffer)。

redolog写入过程

  • 日志最开始会写入位于存储引擎Innodb的redo log buffer,这个是在用户空间完成的。
  • 然后再将日志保存到操作系统内核空间的缓冲区(OS buffer)中。
  • 最后,通过系统调用fsync(),从OS buffer写入到磁盘上的redo log file中,完成写入操作。

图片

redoLog的执行流程-举例说明

我们来看下redo log的执行流程,假设执行的SQL如下:

update T set a =1 where id =666

图片|600

  1. MySQL客户端将请求语句update T set a =1 where id =666,发往MySQL Server层。
  2. MySQL Server 层接收到SQL请求后,对其进行分析、优化、执行等处理工作,将生成的SQL执行计划发到InnoDb存储引擎层执行。
  3. InnoDb存储引擎层会记录下修改操作到内存中。
  4. 记录到内存以后会修改redo log 的记录,会在添加一行记录,其内容是需要在哪个数据页上做什么修改。
  5. 此后,将事务的状态设置为prepare ,说明已经准备好提交事务了。
  6. 等到MySQL Server层处理完事务以后,会将事务的状态设置为commit,也就是提交该事务。
  7. 在收到事务提交的请求以后,redo log会把刚才写入内存中的操作记录写入到磁盘中,从而完成整个日志的记录过程。

innodb_flush_log_at_trx_commit 配置

redo log buffer写入到redo log file,是经过OS buffer中转的。其实可以通过参数innodb_flush_log_at_trx_commit进行配置,参数值含义如下:

0:称为延迟写,事务提交时不会将redo log buffer中日志写入到OS buffer,而是每秒写入OS buffer并调用写入到redo log file中。

1:称为实时写,实时刷。事务每次提交都会将redo log buffer中的日志写入OS buffer并保存到redo log file中。

2:称为实时写,延迟刷。每次事务提交写入到OS buffer,然后是每秒将日志写入到redo log file。这种方式的性能和安全性都介于前两者中间。

innodb_flush_log_at_trx_commit=0

0时,如果MySQL挂了或宕机可能会有1秒数据的丢失。

innodb_flush_log_at_trx_commit=1

1时, 只要事务提交成功,redo log记录就一定在硬盘里,不会有任何数据丢失。

如果事务执行期间MySQL挂了或宕机,这部分日志丢了,但是事务并没有提交,所以日志丢了也不会有损失。

innodb_flush_log_at_trx_commit=2

2时, 只要事务提交成功,redo log buffer中的内容只写入文件系统缓存(page cache)。

如果仅仅只是MySQL挂了不会有任何数据丢失,但是宕机可能会有1秒数据的丢失。

redolog刷盘情况

InnoDB 将 redo log 刷到磁盘上有几种情况:

  1. 事务提交:当事务提交时,log buffer 里的 redo log 会被刷新到磁盘(可以通过innodb_flush_log_at_trx_commit参数控制,后文会提到)。
  2. log buffer 空间不足时:log buffer 中缓存的 redo log 已经占满了 log buffer 总容量的大约一半左右,就需要把这些日志刷新到磁盘上。
  3. 事务日志缓冲区满:InnoDB 使用一个事务日志缓冲区(transaction log buffer)来暂时存储事务的重做日志条目。当缓冲区满时,会触发日志的刷新,将日志写入磁盘。
  4. Checkpoint(检查点):InnoDB 定期会执行检查点操作,将内存中的脏数据(已修改但尚未写入磁盘的数据)刷新到磁盘,并且会将相应的重做日志一同刷新,以确保数据的一致性。
  5. 后台刷新线程:InnoDB 启动了一个后台线程,负责周期性(每隔 1 秒)地将脏页(已修改但尚未写入磁盘的数据页)刷新到磁盘,并将相关的重做日志一同刷新。
  6. 正常关闭服务器:MySQL 关闭的时候,redo log 都会刷入到磁盘里去。

总之,InnoDB 在多种情况下会刷新重做日志,以保证数据的持久性和一致性。

另外,InnoDB 存储引擎有一个后台线程,每隔1 秒,就会把 redo log buffer 中的内容写到文件系统缓存(page cache),然后调用 fsync 刷盘。

也就是说,一个没有提交事务的 redo log 记录,也可能会刷盘。

因为在事务执行过程 redo log 记录是会写入redo log buffer 中,这些 redo log 记录会被后台线程刷盘。

除了后台线程每秒1次的轮询操作,还有一种情况,当 redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动刷盘。

redolog日志文件组

硬盘上存储的 redolog 日志文件不只一个,而是以日志文件组的形式出现,每个的redo日志文件大小都是一样的。

比如可以配置为一组4个文件,每个文件的大小是 1GB,整个 redo log 日志文件组可以记录4G的内容。

它采用的是环形数组形式,从头开始写,写到末尾又回到头循环写,如下图所示。

日志文件组中有两个重要的属性,分别是 write pos、checkpoint

  • write pos:是当前记录的位置,一边写一边后移(写入磁盘的数据页的逻辑序列位置)
  • checkpoint:表示刷盘(写入磁盘)后对应的位置,往后推移
  • write pos到check point之间的部分用来记录新日志,也就是留给新记录的空间。
  • check point到write pos之间是待刷盘的事务,如果不刷盘会被新记录覆盖。

每次刷盘 redolog 记录到日志文件组中,write pos 位置就会后移更新。

当日志文件组的事务正确提交应用后,不再需要保留信息来恢复到之前的状态,此时会清空对应写入过的 redolog 记录,并把 checkpoint 后移更新。

如果 write pos 追上 checkpoint ,表示日志文件组满了,这时候不能再写入新的 redo log 记录,MySQL 得停下来,清空一些记录,把 checkpoint 推进一下。

在 MySQL 8.0.30 之前可以通过 innodb_log_files_in_group 和 innodb_log_file_size 配置日志文件组的文件数和文件大小,但在 MySQL 8.0.30 及之后的版本中,这两个变量已被废弃,即使被指定也是用来计算 innodb_redo_log_capacity 的值。而日志文件组的文件数则固定为 32,文件大小则为 innodb_redo_log_capacity / 32 。

redoLog-保证crash safe机制

当数据库发生宕机重启后,可通过 redolog将未落盘的数据(check point之后的数据)恢复,保证已经提交的事务记录不会丢失,这种能力称为crash-safe。