玩命加载中 . . .

20-undo日志


20.2 事务id

  • 对于只读事务来说,只有在它第一次对某个用户创建的临时表执行增、删、改操作时才会为这个事务分配一个事务id,否则的话是不分配事务id的

  • 对于读写事务来说,只有在它第一次对某个表(包括用户创建的临时表)执行增、删、改操作时才会为这个事务分配一个事务id,否则也是不分配事务id的

聚簇索引的记录除了会保存完整的用户数据以外,而且还会自动添加名为trx_idroll_pointer的隐藏列,如果用户没有在表中定义主键以及UNIQUE键,还会自动添加一个名为row_id的隐藏列

20.3 undo日志的格式

undo日志被记录到类型为FIL_PAGE_UNDO_LOG的页面中

20.3.1 INSERT操作对应的undo日志

roll_pointer本质上就是一个指向记录对应的 undo 日志的一个指针

聚簇索引记录被存储到了类型为FIL_PAGE_INDEX的页面中,undo 日志被记录到类型为FIL_PAGE_UNDO_LOG的页面中

20.3.2 DELETE操作对应的undo日志

插入到页面中的记录会根据记录头信息中的next_record属性组成一个单向链表,称为正常记录链表

被删除的记录也会根据记录头信息中的next_record属性组成一个链表,只不过这个链表中的记录占用的存储空间可以被重新利用,称这个链表 为垃圾链表

Page Header部分有一个称之为PAGE_FREE的属性,它指向由被删除记录组成的垃圾链表中的头节点

假设现在我们准备使用DELETE语句把正常记录链表中的最后一条记录给删除掉,删除的过程需要经历两个阶段

  1. 仅仅将记录的delete_mask标识位设置为1,把这个阶段称之为delete mark

  2. 当该删除语句所在的事务提交之后,会有专门的线程后来真正的把记录删除掉。所谓真正的删除就是把该记录从正常记录链表中移除,并且加入到垃圾链表中,然后还要调整一些页面的其他信息,这个阶段称之为purge

将被删除记录加入到垃圾链表时,实际上加入到链表的头节点处,会跟着修改PAGE_FREE属性的值

20.3.3 UPDATE操作对应的undo日志

1、不更新主键

在不更新主键的情况下,又可以细分为被更新的列占用的存储空间不发生变化和发生变化的情况

  • 就地更新

对于被更新的每个列来说,如果更新后的列和更新前的列占用的存储空间都一样大,那么就可以进行就地更新,也就是直接在原记录的基础上修改对应列的值。

要求每个列在更新前后占用的存储空间一样大,有任何一个被更新的列更新前比更新后占用的存储空间大,或者更新前比更新后占用的存储空间小都不能进行就地更新

  • 先删除掉旧记录,再插入新记录

在不更新主键的情况下,如果有任何一个被更新的列更新前和更新后占用的存储空间大小不一致,那么就需要先把这条旧的记录从聚簇索引页面中删除掉,然后再根据更新后列的值创建一条新的记录插入到页面中

这里所说的删除并不是delete mark操作,而是真正的删除掉,也就是把这条记录从正常记录链表中移除并加入到垃圾链表中,并且修改页面中相应的统计信息。真正删除之后紧接着就要根据各个列更新后的值创建的新记录插入

2、更新主键

在聚簇索引中,记录是按照主键值的大小连成了一个单向链表的,如果我们更新了某条记录的主键值,意味着这条记录在聚簇索引中的位置将会发生改变

针对UPDATE语句中更新了记录主键值的这种情况,InnoDB 在聚簇索引中分了两步处理

  1. 将旧记录进行delete mark操作

  2. 根据更新后各列的值创建一条新记录,并将其插入到聚簇索引中

20.3.4 增删改操作对二级索引的影响

  • 对旧的二级索引记录执行delete mark操作
  • 根据更新后的值创建一条新的二级索引记录,然后在二级索引对应的B+树中重新定位到它的位置并插入

20.5 FIL_PAGE_UNDO_LOG页面

FIL_PAGE_UNDO_LOG类型的页面是专门用来存储 undo 日志的

undo 日志被分为两个大类:

  • TRX_UNDO_INSERT:insert undo日志,提交之后可以直接删除
  • TRX_UNDO_UPDATE:update undo日志

20.6 undo页面链表

第一个 undo 页面称为 first undo page,其余页面称为 normal undo page

同一个 undo 页面要么只存储TRX_UNDO_INSERT大类的 undo 日志,要么只存储TRX_UNDO_UPDATE大类的 undo 日志,不能混着存储,所以一个称为 insert undo 链表,另一个称为 update undo 链表

不同事务执行过程中产生的 undo 日志需要被写入到不同的 undo 页面链表中

20.8 重用undo页面

事务提交后在某些情况下重用该事务的 undo 页面链表

  • 该链表中只包含一个 undo 页面
  • 该 undo 页面已经使用的空间小于整个页面空间的3/4

两种链表在被重用时的策略是不同的

  • insert undo 链表中只存储类型为TRX_UNDO_INSERT_REC的 undo 日志,这种类型的 undo 日志在事务提交之后就没用了,就可以被清除掉。所以在某个事务提交后,重用这个事务的 insert undo 链表时,可以直接把之前事务写入的一组 undo 日志覆盖掉,从头开始写入新事务的一组 undo 日志
  • update undo 链表:在一个事务提交后,它的 update undo 链表中的 undo 日志也不能立即删除掉(用于MVCC)。所以如果之后的事务想重用 update undo 链表,就不能覆盖之前事务写入的 undo 日志。这样就相当于在同一个 undo 页面中写入了多组的 undo 日志

文章作者: kunpeng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kunpeng !
  目录