为什么需要redo日志和undo日志呢?我们知道事务有四个特性:原子性,一致性,隔离性,持久性。隔离性由锁机制实现,剩余的三种特性是由redo日志和undo日志来实现的。
redo log称为重做日志,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性。redo log是物理日志,记录的是数据页的物理变化,undo log不是redo log的逆过程。
undo log成为回滚日志,回滚行记录到某个特定版本,用来保证事务的原子性、一致性。undo log是逻辑日志,对事务回滚时,只是将数据库逻辑地恢复到原来的样子。

1. redo日志

在事务提交完成之前把该事务所修改的所有页面都刷新到磁盘,但是这个简单粗暴的做法有些问题:

  1. 修改量与刷新磁盘工作量不成比例;
  2. 随机IO刷新较慢。

MySQL采用了WAL技术(Write-Ahead Logging),简单理解就是先写日志,再同步事务,因为写日志更快,日志写入成功,就象征了事务提交成功。后续即使服务器宕机了,可以通过redo日志恢复,保证了持久性。因此redo日志有这些优点:

  1. 降低了刷盘频率;
  2. redo占用空间小。

mysql undo文件 mysql的undo和redo_学习

1.1 redo的组成

redo由两部分组成,重做日志的缓冲(redo log buffer),保存在内存中,是易失的,但是读写快;重做日志文件(redo log file),保存在磁盘中,是持久的。redo log buffer对应缓冲大小参数为:innodb_log_buffer_size,默认16M,最小修改1M,最大修改4096M。

1.2 redo整体流程

mysql undo文件 mysql的undo和redo_回滚_02

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

1.3 redo写入redo log buffer的过程

1.4 redo的刷盘策略

redo log buffer的内容是以一定的策略写入磁盘的,redo log buffer刷盘到redo log file的过程也并不是真正的刷到磁盘中去,只是刷入到文件系统缓存(page cache),这是现代操作系统为了提高文件写入效率做的一个优化,因为即使MySQL服务终止了,只要操作系统OS没有终止,那么写入文件系统缓存的数据就没有丢失,而整个系统宕机的概率是比较小的。MySQL通过innodb_flush_log_at_trx_commit这个参数来控制不同刷盘策略。
设置为0 :表示每次事务提交时不进行刷盘操作。(系统默认master thread每隔1s进行一次重做日志的同步)
设置为1 :表示每次事务提交时都将进行同步,刷盘操作( 默认值 )
设置为2 :表示每次事务提交时都只把 redo log buffer 内容写入 page cache,不进行同步。由os自己决定什么时候同步到磁盘文件。
效率排行:2>0>1
安全性排行:1>2>0

2. undo 日志

redo log是事务持久性的保证,undo log是事务原子性的保证。在事务中 更新数据的前置操作其实是要先写入一个 undo log 。undo log可以保证事务原子性:
情况一:事务执行过程中可能遇到各种错误,比如服务器本身的错误 , 操作系统错误 ,甚至是突然断电导致的错误。
情况二:程序员可以在事务执行过程中手动输入 ROLLBACK 语句结束当前事务的执行。
使用undo日志回滚,使得事务看起来什么都没做,保证了事务的原子性。

2.1 undo 日志作用

作用1:回滚数据
这里有一个常见误解:undo用于将数据库物理地恢复到执行语句或事务之前的样子。其实undo是逻辑日志,只是将数据库逻辑地恢复到事务之前的样子,所有修改都被逻辑地取消了,但是数据结构和页可能和之前是不一样的。这是因为在多用户并发系统中,数据库主要任务是协调对数据的并发访问。例如一个事务修改一个页中的数据,另一个事务也在修改数据,因此一个事务回滚只能逻辑上恢复到之前的状态,不能在物理上恢复到事务开始的样子,这样会影响其他事务正在进行的工作。
作用2:MVCC
MVCC的实现是通过undo日志来完成的,当用户读取一行记录时,如果该记录已经被其他用户占用,当前用户可以通过undo日志读取之前版本的信息,从此实现非锁定读取。

2.2 undo 日志类型

undo log分为两种:
insert undo log
是指insert操作前出现的日志,因为insert的操作对事务本身可见,对其他事务不可见(这是隔离性的要求),因此insert undo log在提交后直接删除,不需要purge进行清理。
update undo log
是指update和delete操作前出现的日志,因为update和delete对其他事务可见,可能需要提供MVCC机制,因此不能在事务提交时删除,提交时放入undo链表,等purge线程删除。

2.3 undo 存储结构

2.3.1 回滚段与undo页

undo log采用回滚段管理,每个回滚段包含1024个undo log segment,在每个undo log segment中进行undo页申请。在 InnoDB1.1版本之前 (不包括1.1版本),只有一个rollback segment,因此支持同时在线的事务限制为 1024 。虽然对绝大多数的应用来说都已经够用。从1.1版本开始InnoDB支持最大 128个rollback segment ,故其支持同时在线的事务限制提高到了 128*1024 。

2.3.2 回滚段与事务

每个事务只会使用一个回滚段,一个回滚段在同一时刻可能会服务于多个事务。当一个事务开始的时候,会制定一个回滚段,在事务进行的过程中,当数据被修改时,原始的数据会被复制到回滚段。在回滚段中,事务会不断填充盘区,直到事务结束或所有的空间被用完。如果当前的盘区不够用,事务会在段中请求扩展下一个盘区,如果所有已分配的盘区都被用完,事务会覆盖最初的盘区或者在回滚段允许的情况下扩展新的盘区来使用。回滚段存在于undo表空间中,在数据库中可以存在多个undo表空间,但同一时刻只能使用一个undo表空间.当事务提交时,InnoDB存储引擎会做以下两件事情:

  1. 将undo log放入列表中,以供之后的purge操作
  2. 判断undo log所在的页是否可以重用,若可以分配给下个事务使用

2.3.3 回滚段中的数据分类

未提交的回滚数据(uncommitted undo information)
该数据关联的事务并未提交,用于实现读一致性,所以该数据不能被其他事务的数据覆盖。
已经提交但未过期的回滚数据(committed undo information)
该数据关联的事务已经提交,但是仍受到undo retention参数的保持时间的影响。
事务已经提交并过期的数据(expired undo information)
事务已经提交,而且已经过了undo retention保持时间的限制,属于已经过期的数据,当回滚段满了之后,会优先覆盖“事务已经提交并过期的数据”。

3. redo log和undo log先后顺序

mysql undo文件 mysql的undo和redo_回滚_03