美文网首页
mysql-重做日志

mysql-重做日志

作者: 树生1995 | 来源:发表于2022-07-08 09:27 被阅读0次

    我们都知道操作内存的速度远比操作磁盘的速度要快,所以为了提高执行效率,Mysql会将一部分数据页和索引页提前加载到内存(BufferPool)中,直接对内中的页进行操作,在合适的时候再将内存中的页写回磁盘。

    但是这样会使我们面临一个问题,当服务器宕机时已提交的事务还没来得及将内存中的页写回到磁盘中会导致内存中页的数据丢失,为了解决这个问题我们能想到可以在提交事务的同时将内存中的页写回磁盘,这样虽然能够解决丢数据问题,但这样如果一个事务只操作了某页中的一条记录也需要将整个页写回磁盘这岂不是很浪费 IO,再比如一个事务同时对不连续的很多页进行了修改那么在写回磁盘时会很慢(随机写磁盘的速度比顺序写的速度要慢很多);所以 Mysql 没有使用这种方式来解决数据丢失问题,而是选用重做日志(redolog)的方式解决这个问题,redolog 中记录了我们对哪个表空间哪个页进行了哪些操作等内容。

    同样 Mysql 也不会每生成一条日志就往磁盘写一条而是先写到内存中,在启动的时候 Mysql 默认会为 redolog 志申请16M的内存缓冲区(log buffer),缓冲区中的日志也是以页的形式存放的,为了与索引页区分我们称这类页叫 block,每个 block 有 512 字节的空间用来存储日志。

    在提交事务的同时数据库会将 redlog 写回磁盘中,这样如果服务器宕机即使内存中的数据未及时写回磁盘在重启之后数据库仍然能够通过磁盘中的 redolog 做故障恢复,Mysql 就是通过这种方式保证的持久化。Mysql 还会在其他情况下将日志写到磁盘,如:log buffer 空间不足、服务器正常关闭、做 checkpoint 时、后台有一个线程每隔一秒会将 log buffer 中的日志写回磁盘。

    当我们每往叶子节点代表的数据页中插入一条记录会有其他很多地方都会跟着更新,譬如 PageDirectory 中的槽信息、PageHeader 中的各种页面统计信息、上一条记录中的 next_record 属性等等,再拿一个更极端的情况举例,比如我们执行了一条语句涉及到了很多数据页的修改,那可想而知这样会导致日志比数据页还要大,所以为了解决这个问题 Mysql 在 redolog 中使用字节标识来代表对页的操作,这样可以最大程度减小日志的大小,在需要使用 redolog 重做数据时 mysql 会把 redolog 作为参数传递给一个函数,函数会根据日志的各种标记还原出具体的步骤并执行从而达到重做数据的效果。

    当我们向索引页插入一条记录时可能会产生多条 redolog(譬如:某张表的主键不是顺序生成的,那么在插入新生的的记录时可能会导致索引页分裂、数据记录链表的变动等情况),这条新插入的记录要么插入成功,要么插入失败,这个过程称为MTR(Mini-Transaction),所以这组日志在做崩溃恢复的时候,是一个不可分割的整体。所以并不是每生成一条 redolog 就将其插入到 log buffer 中,而是将每个 MTR 运行过程中产生的日志暂存在一个地方,当 MTR 结束的时候,再将此过程中产生的 redolog 全部复制到 log buffer 的 block 中。


    image.png

    log buffer 在刷盘时需要知道下一需要刷盘的日志在 log buffer 中的哪个位置和下一组日志要刷盘到磁盘日志文件的哪个位置,Mysql 为此定义了两个全局变量分别为 buf_next_to_write 和 lsn (log sequence number) ,在 log buffer 刷盘时是以 MTR 为最小单位进行刷盘的。


    image.png

    Mysql的数据目录下默认有名为 ib_logfile0 和 ib_logfile1 的两个文件,log buffer 中的日志默认会刷新到这两个磁盘文件中,当这两个文件写满了就会接着写到 ib_logfile2 文件中,如果最后一个满了那就重新转到 ib_logfile0 继续写(因为 redo 日志磁盘文件是有限的所以不得不循环利用这些文件)。

    可以看到有序磁盘文件容量有限,所以 Mysql 是循环使用这些磁盘文件的,但是这样很容易导致‘追尾’问题,这是我们应该能够想到 redo 日志只是为了在系统崩溃后恢复脏页用的,如果脏页已经刷新到磁盘中,那么即使系统崩溃,在重启后也用不着使用 redo 日志恢复该页面,所以 redo 日志也就没必要存在了。Mysql 使用一个 checkpoint_lsn 来表示当前系统中哪些日志可以被覆盖(每当有脏页被刷新到磁盘上时,系统就会重新计算一次 checkpoint_lsn,我们把这个过程称作 checkpoint)


    image.png

    我们可是使用 show engine innodb status命令来查看当前 InnoDB存储引擎中各种值的情况,比如:

    mysql> show engine innodb status
    
    Log sequence number 123376971
    Log flush up to 124099769
    Page flushed up to 124052503
    Last checkpoint at 124052494
    
    • Log squence number 表示系统中lsn值,也就是当前系统已经写入的 redo 日志量,包括写入到 log buffer 中的 redo 日志
    • Log flushed up to 表示 flushed_to_disk_lsn 的值,也就是当前系统已经写入磁盘的 redo 日志量
    • Page flushed up to 表示 flush 链表中最早被修改的那个页面对应的 oldest_modification 属性值
    • Last checkpoint at 表示当前系统的 checkpoint_lsn 值

    问:日志还未写入磁盘就宕机了怎么办?

    1. 事务提交时会触发日志的刷盘
    2. 如果日志刷盘不成功,那么事务也会提交失败
    3. 如果事务未提交,日志未刷盘,这种情况不会有什么影响

    加了一层 redolog 减少了写盘数据量和频率,而且还是顺序写,真是应了那句话,没有什么时加一层解决不了的

    相关文章

      网友评论

          本文标题:mysql-重做日志

          本文链接:https://www.haomeiwen.com/subject/cngmbrtx.html