zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

【MySQL系列】- 浅析undo log

mysqllog 系列 浅析 undo
2023-06-13 09:12:59 时间

MySQL事务特性之一就是要保证原子性,一组SQL要么全部成功、要么全部失败。当事务进行过程中,如果出现失败或者异常情况要进行回滚,回到之前最初的样子,要这样实现就要需要把之前的数据记录下来。

undo log 是什么

undo log可以称为撤销日志、undo 日志,它记录着事务回滚前的数据。

官方定义:

❝A storage area that holds copies of data modified by active transactions. ❞

翻译过来就是undo log是保存活动事务修改的数据副本的存储区域。

undo 日志只记录事务中的增删改操作,查询并不会记录,因为查询不会修改数据。undo 日志存在于undo日志段中,undo日志段包含在回滚段中。

undo日志段

undo日志段(undo log segment)是undo日志的集合。undo 日志段包含在回滚段中,一个undo日志段可能包含来自多个事务的undo日志,一个undo日志段一次只能被一个事务使用,但是在事务提交或回滚时,释放undo日志段以后可以重用它。undo日志段也可以被称为“undo segment(undo 段)”。

回滚段

回滚段(rollback segment)是包含undo日志的存储区域。回滚段通常位于系统表空间中。从MySQL 5.6开始,回滚段可以存储在undo表空间中,从MySQL 5.7开始,回滚段也被分配到全局临时表空间。

InnoDB最多支持128个回滚段,其中有1个回滚段用于系统表空间,32个回滚段位于临时表空间,剩余的95个用于undo表空间。

可以通过参数innodb_rollback_segments修改回滚段的个数,默认是最大值128。要分配多余的回滚段给undo表空间的话,回滚段在修改的时候值要大于33。

  • 如果innodb_rollback_segments配置的回滚段个数小于等于32,InnoDB仍然会分配一个回滚段给系统表空间,32个回滚段给临时表空间,也就是说配置无效。
  • 如果innodb_rollback_segments配置的回滚段个数大于32,InnoDB会分配一个回滚段给系统表空间,32个回滚段给临时表空间,其余的用于undo表空间。如果undo表空间不存在,则其余的用于系统表空间。

undo slot

回滚段实际上是一种Undo 文件组织方式,每个回滚段又由多个undo slot组成。回滚段支持的事务数取决于回滚段中的撤销槽(undo slot)位数和每个事务所需的undo日志数。根据InnoDB页面的大小,回滚段中undo槽的数量是不同的。

InnoDB页大小

Undo Slot在回滚的中的数量(InnoDB页大小 / 16)

4096 (4KB)

256

8192 (8KB)

512

16384 (16KB)

1024

32768 (32KB)

2048

65536 (64KB)

4096

InnoDB页大小默认为16k,所以Undo Slot数量默认为1024。

事务产生的undo日志

一个事务在以下4种情况会产生undo日志,分别为:

  1. 在用户自定义的常规表上的INSERT操作
  2. 在用户自定义的常规表上的UPDATE和DELETE操作
  3. 在用户自定义的临时表上的INSERT操作
  4. 在用户自定义的临时表上的UPDATE和DELETE操作

可以发现,大多数对数据的变更操作包括INSERT/DELETE/UPDATE,总的还是分为两类undo 日志,INSERT操作独自占一类,UPDATE和DELETE归为一类。其中INSERT操作在事务提交前只对当前事务可见,因此产生的Undo日志可以在事务提交后直接删除,INSERT操作产生的undo日志称为insert undo ,而对于UPDATE/DELETE则需要维护多版本信息,在InnoDB里,UPDATE和DELETE操作产生的Undo日志被归成一类,称为update undo。

undo日志会按需分配,需要哪类日志就用哪类。比如,如果一个事务同时有INSERT、UPDATE和DELETE操作,那么这4种情况的undo日志就都需要。如果一个事务只有INSERT操作,那就只需要一种undo日志。

事务如果操作的是用户自定义的常规表,那undo日志就会从系统表空间或者undo表空间的回滚段分配。反之,如果事务操作的是用户定义的临时表,那么undo日志就会从临时表空间的回滚段分配。

InnoDB 支持的并发读写事务数量

根据回滚段可以预估出InnoDB 能支持多少个并发读写事务:

  • 当事务对临时表进行操作时,InnoDB能够支持的并发读写事务的数量受限于分配给临时表空间的回滚段的数量,即32个。
  • 如果一个事务在常规表上执行INSERT、UPDATE或DELETE操作中的一个,那么支持的并发读写事务数量计算公式为:
(innodb_page_size / 16) * (innodb_rollback_segments - 32)
  • 如果一个事务在常规表上执行INSERT和UPDATE或DELETE操作中的一个,那么支持的并发读写事务数量计算公式为:
(innodb_page_size / 16 / 2) * (innodb_rollback_segments - 32)
  • 如果一个事务在临时表上执行INSERT、UPDATE或DELETE操作中的一个,那么支持的并发读写事务数量计算公式为:
(innodb_page_size / 16) * 32
  • 如果一个事务在临时表上执行INSERT和UPDATE或DELETE操作中的一个,那么支持的并发读写事务数量计算公式为:
(innodb_page_size / 16 / 2) * 32

undo日志作用

undo 日志主要有两个作用,事务回滚和作为MVCC的一部分:

  1. 事务回滚

上面说的就是事务回滚,主要用于保证事务的原子性。通过undo日志在事务需要回滚的时候,让数据恢复到最初的样子。

  1. 作为MVCC的一部分

undo日志作为MVCC(Multi-Versioin Concurrency Control)版本链的一部分,Undo记录中存储的是老版本数据,当一个旧的事务需要读取数据时,为了能读取到老版本的数据,需要顺着undo链找到满足其可见性的记录。

undo日志机制

对于INSERT操作,insert undo在事务提交后,ndo segment就释放了。而对于UPDATE/DELETE操作产生的update undo则比较复杂,。

  • DELETE操作:在执行一条DELETE语句时,在事务提交后,并不会直接删除而是会将记录delete_mask标识位设置为1,并加入history list,由后台线程Purge进行清理。
  • UPDATE操作:执行UPDATE语句又分为两种情况:
    • 存储空间发生变化:这种情况称为就地更新(in-place update)。更新记录时,对于被更新的每个列来说,如果更新前后的列所占用的存储空间都一样大,那么就可以进行就地更新,也就是直接在原记录的基础上修改对应列的值。
    • 存储空间不发生变化:这种情况分为两步: 1、先删除旧记录 2、再插入旧记录。 这里的删除并不是上面所说的将delete标志位设置为1,而是将记录放到history list中,由用户线程同步执行真正的删除操作,真正删除之后紧接着就要根据各个列更新后的值创建的新记录入。 如果新创建的记录占用的存储空间大小不超过旧记录占用的空间,那么可以直接重用被加入到history list中的旧记录所占用的存储空间,否则的话需要在页面中新申请一段空间以供新记录使用,如果本页面内已经没有可用的空间的话,那就需要进行页面分裂操作,然后再插入新记录。
    • 更新主键:这种情况下会分为两步: 1、先将之前记录的delete标志位设置为1,也就是说先删除之前的记录 2、重新插入一条记录。 所以对于更新主键的情况下其实是产生了两条undo日志,一个是delete操作产生的,一个是insert操作产生的。
    • 不更新主键:这种操作下,会根据被更新的列占用的存储空间是否变化分为两种情况:

对于UPDATE/DELETE操作产生的update undo日志,则会加入history list,由后台线程Purge进行清理。

undo日志参数

可通过命令show variables like "%undo%"查看undo日志相关参数:

  1. innodb_undo_directory InnoDB创建undo表空间的路径,通常用于将undo日志放置在不同的存储设备上。如果没有指定,默认会创建在MySQL数据目录下。
  2. innodb_undo_tablespaces undo表空间数量,默认为0。在MySQL8.0版本中已经废弃,需要添加的话可以使用CREATE TABLESPACE语句创建undo表空间。
  3. innodb_undo_log_truncate undo表空间清理开关,默认OFF,关闭。
  4. innodb_max_undo_log_size undo表空间的阈值,默认值为1073741824 bytes(1024 MiB)。如果超过了这个阈值,undo表空间将会进行truncate,如果innodb_undo_log_truncate开启的话。
  5. innodb_undo_logs innodb回滚段的数量,同innodb_rollback_segments,默认值为128。innodb_undo_logs在MySQL 5.7.19版本开始被废弃。

总结

undo log是MySQL最重要的日志之一,这里简单介绍了一下undo日志的概念、作用、机制等,算是对undo日志有了大致的了解。

参考资料:

❝https://dev.mysql.com/doc/refman/5.7/en/innodb-undo-logs.html http://mysql.taobao.org/monthly/2015/04/01/ ❞