【MySQL】事务日志 undo log 详解

时间:2023-02-16 19:14:16

Redo log是事务持久性的保证,Undo log是事务原子性的保证。在事务中更新数据前置操作其实就是要写入Undo log

1.Undo 日志引入:

事务需要保证原子性,也就是事务中的操作要么全部完成,要么什么也不做。但有时候事务执行到一半会出现一些情况,比如:

  • 情况一:事务执行过程中可能遇到各种错误,比如服务器本身的错误,操作系统错误,甚至是突然断电导致的错误。
  • 情况二:程序员可以在事务执行过程中手动输入ROLLBACK语句结束当前事务的执行

以上情况出现,我们需要把数据改回原先的样子,这个过程称之为回滚,这样就可以造成一个假象:这个事务看起来什么都没做,所以符合原子性要求每当我们要对一条记录做改动时(这里的改动可以指INSERT、DELETE、UPDATE),都需要“留一手”一一把回滚时所需的东西记下来比如:

  • 你插入一条记录时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉就好了。(对于每个INSERT,InnoDB存储引擎会完成-一个DELETE)
  • 你删除了一条记录,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了。(对于每个DELETE,InnoDB存储引擎会执行一个INSERT)
  • 你修改了一条记录,至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值就好了。(对于每个UPDATE,InnoDB存储引擎会执行一个相反的UPDATE,将修改前的行放回去)

MySQL把这些为了回滚而记录的这些内容称之为撤销日志或者回滚日志(即undo log)。注意,由于查询操作(SELECT)并不会修改任何用户记录,所以在查询操作执行时,并不需要记录相应的undo日志。此外,undo log会产生redo log,也就是undo log的产生会伴随着redo log的产生,这是因为undo log也需要持久性的保护。


2.Undo 日志作用:

作用一:回滚数据

用户对undo日志可能有误解:undo用于将数据库物理地恢复到执行语句或事务之前的样子。但事实并非如此。undo是逻辑日志,因此只是将数据库逻辑地恢复到原来的样子。所有修改都被逻辑地取消了,但是数据结构和页本身在回滚之后可能大不相同。

这是因为在多用户并发系统中,可能会有数十、数百甚至数千个并发事务。数据库的主要任务就是协调对数据记录的并发访问。比如,一个事务在修改当前一个页中某几条记录,同时还有别的事务在对同一个页中另几条记录进行修改。因此,不能将一个页回滚到事务开始的样子,因为这样会影响其他事务正在进行的工作。

作用二:MVCC(多版本并发控制)

undo的另一个作用是MVCC,即在InnoDB存储引擎中MVCC的实现是通过undo来完成。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取


3.Undo 日志存储结构:

3.1 回滚段与undo页:

InnoDB对undo log管理采用段的方式,也就是​​回滚段(Rollback Segment)​​。每个回滚段记录了1024个​​undo log segment​​,而在每个​​undo log segment​​段中进行undo页的申请。

  • 在InnoDB1.1版本之前(不包括1.1版本),只有一个​​rollback segment​​,因此支持同时在线的事务限制为1024。虽然对绝大多数的应用来说都已经够用。
  • 从1.1版本开始InnoDB支持最大128个​​rollback segment​​,故其支持同时在线的事务限制提高到了128*1024
show variables like '%innodb_undo_logs%';

【MySQL】事务日志 undo log 详解

虽然InnoDB1.1版本支持了128个​​rollback segment​​,但是这些​​rollback segment​​都存储于共享表空间ibdata中。从InnoDB1.2版本开始,可通过参数对​​rollback segment​​做进一步的设置。这些参数包括:

【MySQL】事务日志 undo log 详解

undo log 相关参数一般很少改动。

【MySQL】事务日志 undo log 详解

3.2 回滚段与事务:

【MySQL】事务日志 undo log 详解

show variables like '%innodb_undo_tablespaces%';

【MySQL】事务日志 undo log 详解

3.3 回滚段中的数据分类:

【MySQL】事务日志 undo log 详解


4.Undo 日志类型:

【MySQL】事务日志 undo log 详解


5.Undo 日志生命周期:

5.1 事务日志生成过程:

【MySQL】事务日志 undo log 详解

【MySQL】事务日志 undo log 详解

【MySQL】事务日志 undo log 详解

在更新Buffer Pool中的数据之前,我们需要先将该数据事务开始之前的状态写入Undo Log中。假设更新到一般出错了,我们就可以通过Undo Log来回滚到事务开始之前。

【MySQL】事务日志 undo log 详解

【MySQL】事务日志 undo log 详解

【MySQL】事务日志 undo log 详解

【MySQL】事务日志 undo log 详解

【MySQL】事务日志 undo log 详解

【MySQL】事务日志 undo log 详解

5.2 Undo log 回滚:

以上面的例子来说,假设执行rollback,那么对应的流程应该是这样:

  1. 通过undo no=3的日志把id=2的数据删除
  2. 通过undo no=2的日志把id=1的数据的deletemark还原成0
  3. 通过undo no=1的日志把id=1的数据的name还原成Tom
  4. 通过undo no=0的日志把id=1的数据删除

5.3 Undo log 删除:

【MySQL】事务日志 undo log 详解