MySQL(十九)MySQL事务日志(二)UndoLog

MySQL(十九)MySQL事务日志(二)UndoLog

image-20230505152421262

1 undo 日志概述

redo log是事务持久性的保障,而undo log则是事务原子性一致性的保证,如上图,在事务中更新数据的前置操作其实是需要将数据写入到 undo log 方便回滚。

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

  • 情况一:事务执行过程中遇到一些情况,如服务器本身错误、操作系统错误甚至是断电
  • 情况二:程序员自身进行了rollback操作

​ 以上情况出现的时候,mysql就需要把数据恢复成事务之前的状态,这个过程称作回滚,以满足事务原子性的特性。回滚操作并不是直接将数据复原,而是

image-20230505155858035

​ Mysql为了回滚而记录的这些内容称之为撤销日志回滚日志由于查询不会修改任何用户记录,所以在查询操作执行时,不需要记录相应的undo log

​ 此外,undo log也会产生redo log,即undo log的产生会伴随着redo log的产生,这是因为undo log也需要进行持久化的保护。

2 undo log的作用

  • 回滚数据undo log逻辑日志,只是将数据库逻辑地恢复成事务执行之前的状态,所有的操作都被逻辑地取消了,但是数据结构页本身在回滚之后可能大不相同。

    在多用户并发系统中,可能还有成百上千的并发事务在工作,数据库的工作就是协调这些事务对数据的并发访问,因此不能只是简单将数据库回滚到某一个事务执行之前的状态,这样会影响其他运行的事务

  • MVCC:在InnoDB存储引擎MVCC的实现是通过undo log来实现的,当用户读取一行记录的时候,该条记录已经被其他事务占用,那么当前事务可以通过undo log读取之前的行版本信息,以实现非锁定读取。

3 undo log的数据结构

3.1 回滚段与undo 页

InnoDBundo log的管理采用段的方式,也就是回滚段(rollback segment),每个回滚段记录1024个undo log segment,然后会在每个undo log segment中进行undo页的申请。

  • InnoDB 1.1之前只存在一个回滚段,因此只支持1024个在线并发事务,1.1之后支持128个回滚段,因此可以支持128 * 1024 个并发事务,可以通过innodb_undo_logs查看支持的回滚段个数:

    mysql> show variables like 'innodb_undo_logs'
        -> ;
    Empty set (0.01 sec)
    

​ undo log涉及的相关参数如下,一般情况下不会去动:

image-20230505162649475

​ 当开启一个事务需要写入undo log的时候,就需要去undo log segment中找一个空闲的位置,当有空位的时候就申请一个undo页,然后在这个申请到的undo页上进行undo log记录的写入。

​ 但是为每一个事务都分配一个undo 页是非常浪费的,一个页的大小是16k,假设一个应用的TPS(每秒处理的事务数)是1000,那么1s就需要1000个页即16M,1分钟也就快1G了,除非mysql的清理速度特别快,否则很容易造成空间的浪费。

​ 所以出现了对undo log页的重用,即事务在commit后并不会被立刻删除,而会被放到一个链表上,如果判断页面的使用小于3/4undo页面可以被重用,其他事务的undo log记录可以写在后面。因为undo log的记录离散的,所以磁盘的清理效率不高

3.2 回滚段与事务
  • 每个事务只会使用一个回滚段而每个回滚段可以同时服务于多个事务
  • 当一个事务开始的时候就会指定一个回滚段,在事务进行的过程中,会将被修改数据的原始数据复制到回滚段
  • 在回滚段中,事务会不断填充盘区,直到事务结束或者盘区空间用完。如果当前盘区不足,则会在段中申请下一个盘区,申请不到则会覆盖使用最初的盘区。
  • 回滚段存在于undo表空间中,在数据库中存在多个undo表空间,但在某一个时刻只能使用一个undo表空间

​ 当事务提交的时候,innodb会进行下面的操作:

  • undo log放入链表中,供后面的purge操作(后台清理线程)
  • 判断undo log是否可以重用,可以则分配给下一个事务使用
3.3 回滚段中的数据分类
  1. 未提交的回滚数据:数据对应的事务未提交,为了保证数据的读一致性,所以不能被其他事务覆盖数据
  2. 已提交但是未过期的回滚数据:该数据关联的事务已经提交,但是受到undo retention参数保持时间的影响
  3. 已提交并过期的回滚数据:该数据关联的事务已经提交,并超过了undo retention参数设置,当回滚段满了之后,会被优先覆盖

​ 如果要执行更新操作,会将原纪录放入undo log,并通过隐藏的回滚指针指向undo log中的原纪录。其他事务此时需要查询时,就是查询undo log中这行数据的最后一个历史版本。这也是事务提交之后不能马上删除undo log和undo log所在的页的原因,而是将这些undo log页放到链表,由purge线程判断是否清除

3.4 undo log的类型
  • insert undo log

    ​ 即在insert操作中产生的undo log,因为是添加操作,所以只对当前记录可见,对其他事务不可见(事务隔离性的要求),所以可以直接删除,不需要purge操作

  • update undo log

    ​ 记录的是update 和 delete产生的undo log,update undo log由于还需要提供MVCC机制,因此不能提交完事务就删除,而是将这些undo log页放到undo log链表,由purge线程判断是否清除

4 undo log的生命周期

4.1 redo log + undo log简要生成过程

​ 假设有两个数值A=1和B=2,将A修改为3B修改为4:

image-20230505171017622

这里的redo log在事务提交前刷盘的,应该是由master线程将redo log buffer中的数据写入redo log file的。

  • 1-7宕机,事务未提交不会对数据造成任何影响
  • 8-9宕机,redo log完成了持久化但是数据没有,恢复之后可以选择使用undo log回滚或者继续执行事务
  • 9之后宕机,为了满足持久性redo log在恢复之后会把数据刷回磁盘
image-20230505171526572
4.2 详细生成过程

InnoDB行格式有几个隐藏列:

image-20230505172959788
执行insert操作的时候

​ 比如这是添加一条记录,插入数据会生成一个undo log,并且真实数据的回滚指针会指向这个undo log页中的相应记录undo log会记录undo log的序号、插入主键的列和值那么在进行rollback的时候,直接通过主键的值将该条记录删除就可以了

begin;
insert into user('name') values 'Tom';
image-20230505173151420
执行update操作的时候

​ 比如下面的更新语句,执行完之后会把老的列值写入新的undo log,并且回滚指针指向该新的undo log

update user set name = 'Sun' where id = 1;
image-20230505173537335

​ 而对于更新主键的操作,会把数据的deletemark标记打开,这时候并没有真正删除记录,真正的删除会交给清理线程去判断,然后会在后面插入一条新的数据,新的数据也会产生undo log,并且undo log的序号会递增。每次回滚的时候,只需要按照序号依次向前推,就可以找到原始的数据了。

update user set id = 2 where id = 1;

image-20230505181755470

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的记录删除
undo log的删除

​ 针对于insert undo log,由于插入操作对其他事务不可见,因此可以直接删除;而对于update undo log,由于要提供MVCC机制,因此不能再提交事务的时候就进行删除,而是提交的时候放入undo log链表,由purge线程判断清除。

5 小结

image-20230505182509783

​ undo是逻辑日志,对事务进行回滚,将数据库逻辑地恢复成事务执行之前的状态

​ redo log是物理日志,记录的是数据页的物理变化,redo log不是undo log的逆过程

posted @ 2023-05-09 13:55  Tod4  阅读(136)  评论(0编辑  收藏  举报