MySQL事务详解(五):持久性的保障——redo

redo的场景

事务的持久性要求对于一个已经提交的事务,无论提交后发生什么情况,对数据库的更改都不能丢失。
但是在MySQL的InnoDB引擎中,无论对于数据的访问还是更新,都是在被称为buffer pool的缓冲池中进行了的。
事务提交,意味着数据被写入内存中缓冲池,还未落地到磁盘,
那么当服务器发生故障或者断电等异常后,会导致缓冲池中的数据丢失,违背了持久性。

那么是否可以在提交事务之前,将修改的数据直接刷新到磁盘来避免?
原则上上述简单粗暴的方法可以解决问题,但是会带来两个问题:

  • buffer pool的引入就是为了缓解引擎与磁盘之间的速度差,减少磁盘IO,提高处理速度。
    剔除掉缓冲池解决掉持久性问题,但是带来更大的性能问题,得不偿失。
  • InnoDB以页(默认16K)来管理存储空间,如果修改的页面的一个字节,也要刷新16K的数据,简直太浪费了。

针对上面的场景,提出了redo来解决:

  • 磁盘随机IO带来的速度问题,通过redo日志文件的顺序写入来解决。
  • redo日志只需要记录表空间ID、页号、偏移量以及需要更新的值等,这些数据所需空间很小

redo的组成

redo log buffer

类似于buffer pool的作用,redo也引入了名为redo log buffer的缓冲区,易失性。
通过参数innodb_log_buffer_size可以修改缓冲区的大小,默认16M。

缓冲区被划分为大小为512字节的redo log block,redo日志被顺序写入到这些block中。


redo log

redo日志是以文件组的形式管理的,默认是在MySQL数据目录下(使用show variables like "datadir"查看)。

目录下ib_logfile0和ib_logfile1即为redo日志文件,写入日志时,首先写入ib_logfile0,如果写满在写入ib_logfile1。
依此类推,当最后一个文件也写满时,再重新写入到ib_logfile0。在这里就会考虑到日志文件中的淘汰机制(checkpoint机制),保证不覆盖掉有用的日志。

可以使用MySQL参数来修改与redo log相关的配置:
innodb_log_file_size:每个redo日志文件的大小,默认48M;
innodb_log_group_home_dir:redo日志文件存储目录,默认数据目录;
innodb_log_files_in_group:redo日志文件数量,默认2。

redo流程

  • 磁盘中的数据经过page cache,读取到buffer pool中;
  • MySQL直接修改buffer pool中的数据,同时产出redo日志;
  • 事务提交时,先将redo日志写入到redo log buffer(WAL机制),是否写入到磁盘取决于刷盘策略的配置。
  • 后台线程将buffer pool中的脏页进行刷盘。

刷盘策略

redo log buffer的redo日志会根据不同情况和配置,刷新到磁盘上,实现真正的持久化。

  • log buffer的容量使用占比大约为一半时,就会触发刷盘
  • 后台线程,每秒一次刷盘(并不保证100%的‘每秒’)
  • 服务器正常关闭时,触发刷盘
  • 事务提交时,根据innodb-flush-log-at-trx-commit参数配置选择不同的刷盘策略:
    • innodb-flush-log-at-trx-commit=1(默认):
      当事务提交时,将log buffer中的数据写入到page cache,并调用fsync进行刷盘,效率最低。不会丢失数据。
    • innodb-flush-log-at-trx-commit=0
      当事务提交时,log buffer中的数据写入到page cache和刷盘都以后后台线程,效率最高。可能会丢失数据。
    • innodb-flush-log-at-trx-commit=2
      当事务提交时,只将log buffer中的数据写入到page cache,刷盘依靠后台线程每秒种刷盘一次。可能会丢失数据。

checkpoint机制

redo log的文件组是按顺序写入,当写完最后一个文件时,会再从第一个文件开始写入。
在这种情况下,如果redo log中日志对应的脏页还未刷到磁盘上,那么在系统崩溃时就会导致数据丢失。
因此,需要记录redo日志中已经刷到磁盘的记录,保证循环写不会覆盖掉有用的数据。这个功能就需要checkpoint机制来实现。
checkpoint_lsn:全局变量,系统中可以被覆盖的redo日志总量。

  1. 当脏页被刷到磁盘时,将当前系统中被最早修改的脏页对应的修改值复制给checkpoint_lsn。
  2. 将checkpoint_lsn与当前redo日志文件偏移量等信息写入到日志文件管理信息中。
    经过上两步,即为一次checkpoint。

崩溃恢复

  • 第一步,确认恢复的起点。
    • checkpoint机制保证checkpoint_lsn之前的脏页已全部刷盘,那么之后的数据就是我们要恢复的。
  • 第二步,确认恢复的终点。
    • 通过日志文件block的header中的信息可以知道每个block使用的字节空间,从而确定终点
  • 第三步,依次扫描日志,将对应的页恢复。
    • 通过hash表加快恢复速度:将表空间ID和页号取hash,这样同样表空间和页下的redo日志就可一次查询并恢复。
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容