MySQL事务日志——REDO日志、UNDO日志详解


作者:空白

1. 前言

参考原文链接:跳转

本文将详细介绍MySQL数据库中的REDO日志的作用、实现了什么功能、具体机制和优缺点。需要具有一定的数据库基础、熟悉事务的ACID的前提下可以更好地阅读😄

对于事务,我们知道有原子性、一致性、隔离性和持久性四种特性。

简单概括就是:

  1. 原子性:事务是最小的执行单位,不可再拆分了
  2. 一致性:对于事务的操作,要保证结果的一致。比如一边+了50,另一边减少50,则两边必须都完成or都没完成才可以,不能够只完成一部分。
  3. 隔离性:针对多线程并发情况,要保证一个事务在线程执行的过程中,不被别的线程所干扰、改动数据。
  4. 持久性:就是事务提交了 ,做完了,那这个数据就要持久地保存在磁盘里。不能说提交了结果后面发现数据没在里面,那就乱套了。
    事务的隔离性由 锁机制 实现。
    而事务的原子性、一致性和持久性由事务的 redo 日志和undo 日志来保证。
    REDO LOG 称为 重做日志 ,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性。
    UNDO LOG 称为 回滚日志 ,回滚行记录到某个特定版本,用来保证事务的原子性、一致性。

####1、基础知识——缓冲池Buffer Pool与"刷盘"操作

InnoDB存储引擎操作数据库的时候 ,并不是直接通过sql语句对磁盘进行crud,因为直接操作磁盘会带来大量的 IO,以及效率和速度底下等问题。所以实际上,InnoDB是先将磁盘的数据加载到内存中,然后对内存进行crud以后,再通过checkpoint机制(按照一定的时间间隔进行刷盘)把做好的数据传回磁盘,也就是所谓“刷盘”。

alt

对于这样的操作,虽然速度快了,但可靠性就会出问题。 因为对于内存而言,并不能保证数据的持久化,如果发生断电、宕机的问题,内存中的数据就全都消失了!这样的结果必然是不可以容许的。 例如,对于一个事务,已经做完并提交了,内存中的数据也操作完了,本来此时应该刷盘回磁盘,从而保证持久化,结果这时候数据库宕机了,那么这段数据就丢失了、无法恢复。

    **那么该如何解决这种持久性的问题呢?使用REDO日志!**

2. REDO日志

###2.1 REDO日志的组成

####REDO日志其实包含2部分:

(1)重做日志的缓冲 (redo log buffer),保存在内存中,容易丢失。其参数设置:innodb_log_buffer_size默认16M大小。

(2)重做日志的文件 (redo log file) ,保存在硬盘中,是持久的。

###2.2 REDO日志的基本实现流程 InnodeDB的更新操作采用Write Ahead Log的策略,关键就在于:先写入日志,再写入磁盘,即下图的顺序1、2、3、4。其具体解析如下。

alt

第1步:先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝。 第2步:生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值。 第3步:当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加写的方式。 第4步:定期将内存中修改的数据刷新到磁盘。

##解读

  • 对于前2步,需要注意:update事务每做1条,就要在redo log buffer中更改一条!而不是commit全部事务以后才更新redo log buffer。 这样做的好处是,如果没有commit事务就发生宕机,那就全部数据丢失,但是无所谓!因为反正没commit的事务也不需要保存,丢失就对了!恰恰保证了事物的一致性。
  • 对于第3步,需要注意:事务commit后,就要把内存中的redo内容刷入磁盘中的redo file,后期才方便恢复数据

所以,REDO日志的核心思想就是:如果因为数据库宕机等问题导致内存丢失,但是具体操作已经存入redo file了,下次直接从磁盘的redo file恢复这次事务的操作即可。

3. 2.3 redo log的刷盘策略(即上一小节的第3步)

redo log的写入并不是直接写入磁盘的,InnoDB引擎会在写redo log的时候先写redo log buffer,之后以一定的频率刷入到真正的redo log file 中。这里的一定频率怎么看待呢?这就是我们要说的刷盘策略。

注意,redo log buffer刷盘到redo log file的过程并不是真正的刷到磁盘中去,只是刷入到 文件系统缓存 (page cache)中去(这是现代操作系统为了提高文件写入效率做的一个优化),真正的写入会交给系统自己来决定(比如page cache足够大了)。 那么对于InnoDB来说就存在一个问题,如果交给系统来同步,同样如果系统宕机,那么数据也丢失了(虽然整个系统宕机的概率还是比较小的)。 alt 针对这种情况,InnoDB给出 innodb_flush_log_at_trx_commit 参数,该参数控制 commit提交事务时,如何将 redo log buffer 中的日志刷新到 redo log file 中。它支持三种策略:

设置为0 :表示每次事务提交时不进行刷盘操作。(系统默认master thread每隔1s进行一次重做日志的同步) 设置为1 :表示每次事务提交时都将进行同步,刷盘操作( 默认值 ) 设置为2 :表示每次事务提交时都只把 redo log buffer 内容写入 page cache,不进行同步。由os自己决定什么时候同步到磁盘文件。(这种策略,就算数据库宕机了,但只要操作系统不挂掉,那么数据仍然会从page cache刷盘进去。)

4. 基础知识:事务的回滚

事务需要保证 原子性 ,也就是事务中的操作要么全部完成,要么什么也不做。 但有时候事务执行到一半会出现一些情况,比如: 情况一:事务执行过程中可能遇到各种错误,比如 服务器本身的错误 , 操作系统错误 ,甚至是突然断电导致的错误。 情况二:程序员可以在事务执行过程中手动输入 ROLLBACK 语句结束当前事务的执行。 以上情况出现,我们需要把数据改回原先的样子,这个过程称之为 回滚

5. UNDO日志

相对于REDO日志文件记录的是update以后的数据,UNDO日志是用于记录update之前的数据(因为UNDO日志的作用就是恢复update之前的数据嘛,即回滚到之前的状态)。

完整的demo项目,请关注公众号“前沿科技bot“并发送"状态机"获取。

alt

扫码或搜索:前沿科技
发送 290992
即可立即永久解锁本站全部文章