mysql使用redo日志实现事务持久性

178 阅读3分钟

概述

前面提到,为了减少io操作,加载的数据页先会放到Buffer pool,等到刷盘时机,才会真实将页数据刷到磁盘进行持久化.假设修改好的脏页,在事务已经提交的情况下,突然断电,那么修改的脏页就丢失了,这样没办法保证事务的持久性.这个时候redo日志就派上用场了.

redo日志原理

image.png

1.当执行一条写语句时候,首先会加载到Buffer pool的缓存页中,然后修改页中数据,加入到
  flush链表中,我们知道如果这个时候断电,数据其实在只是内存中改了,会导致数据丢失,
  这个修改后的脏页再也无法恢复出来了。
  
2.加入到flush链表中,其实马上会写入到一个叫redo日志文件中,当然不是直接,直接就太慢了,
  其实是先写到一个叫做log buffer中,每次都会写入一个mtr(不可分割的日志组),可以理解为一个日志组。
  写入多少字节,那么这个lsn就记录偏移量。
  
3.如果这个时候断电了,还是会导致log buffer的数据丢失,还是不能保证持久性。所以,其实会在一些时机
  去将log buffer的mtr刷新到真实的物理文件log file中。
  
4.首先如果一个事务提交了,或者log buffer的,或者一个后台线程刷。三种情况下,都会迫使log buffer中的
  mtr刷到真实log file文件中,这个时候断电,那么在非极端情况下,日志早就被刷到log file中了,这样就保证
  了事务的持久性了。
  
5.log file文件是有限的,也就是说如果log file都记满了,那么会覆盖之前刷盘的日志。假设只有 log_file1和
  log_file2,那么写完log_file2,接下来落盘数据会覆盖之前的log file,导致数据丢失,无法恢复。这样其实就
  需要一个检查机制,检查哪些是可以覆盖,没用的redo log,那么覆盖就没任何影响。
  
6.redo中的checkpoint就是这个作用。那么哪些条件是符合可以覆盖的redo log呢? 满足的条件就是,那个mtr已经
  从flush链表移除的,也就是数据被完全改完了的,不可能存在数据再变动的页。这个时候,在log file中做
  checkpoint时候,使用check_point_lsn进行标识。check_point_lsn之后的mtr,即使已经落盘,但是数据还在
  脏页中,也就是状态还会变。
  
7.那么假设如上图断电了,mtr1页中数据因为已经完全不在内存中了,肯定已经保存在对应表空间了。但是mtr2其实
  还是在flush page中的,但是人家日志已经刷到log file了,那么直接加载log file 中mtr2的日志到flush链
  中,这个时候数据就恢复到了断电前的状态了。其实按照log buffer的刷新机制,一般来说,mtr3,mtr4,mtr5
  大概率是会被刷到log file的,其实也是大概率可以恢复的,这个取决于你设置的log buffer的刷盘机制。

总结

redo log 解决了一个比较重要的问题,那就是保证事务持久性。将内存中的脏页记录的日志组,即时记录到redo log ,然后使用redo log重新加载页数据到Buffer pool中,从而恢复到了断电前的Buffer pool状态