MySQL Undo Log

总结自bojiangzhou

undo log称为撤销日志或回滚日志。在一个事务中进行增删改操作时,都会记录对应的 undo log。在对数据库进行修改前,会先记录对应的 undo log,然后在事务失败或回滚的时候,就可以用这些 undo log 来将数据回滚到修改之前的样子。

InnoDB 在内存维护了一个全局变量来表示事务ID,每当要分配一个事务ID时,就获取这个变量值,然后把这个变量自增1

在行记录格式中行记录格式,行记录中会有三个隐藏列:

  • DB_ROW_ID:如果没有为表显式的定义主键,并且表中也没有定义唯一索引,那么InnoDB会自动为表添加一个row_id的隐藏列作为主键。

  • DB_TRX_ID:事务中对某条记录做增删改时,就会将这个事务的事务ID写入trx_id中。

  • DB_ROLL_PTR:回滚指针,本质上就是指向 undo log 的指针。

Undo Log Type

每对一条记录做一次改动,就会产生1条或者2undo log。一个事务中可能会有多个增删改SQL语句,这些 undo log 会被从 0 开始递增编号,这个编号称为 undo no

insert

插入一条数据对应的undo操作其实就是根据主键删除这条数据就行了。所以 insert 对应的 undo log 主要是把这条记录的主键记录上。

比如我们开启了一个事务,向 account 中插入两条数据:

BEGIN; 
INSERT INTO account(id,card,balance) VALUES (1, 'AA', 0),(2, 'BB', 0);

假设这个事务的事务ID为100,这条INSERT语句会插入两条数据,就会产生两个 undo log。插入记录的时候,会在行记录的隐藏列事务ID中写入当前事务ID,并产生 undo log,记录中的回滚指针会保存 undo log 的地址。而同一个页中的多条记录会通过next_record连接起来形成一个单链表,这块可以参考前面的行记录格式和数据页结构相关的文章。

delete

删除一条数据大致可以分为两个阶段:

  • 阶段一

首先是用户线程执行删除时,会先将记录头信息中的 delete_mask 标记为 1,而不是直接从页中删除,因为可能其它并发的事务还需要读取这条数据。(后面讲MVCC的时候就知道为什么了)

  • 阶段二

提交事务后,后台有一个 purge 线程会将数据真正删除。

首先要知道,页中的数据是通过记录头信息中的 netx_record 连接起来的单向链表(假设这个链表称为数据链表)。页中还有另一个链表,称为垃圾链表,记录真正删除后,会从数据链表中移除,然后加入到垃圾链表的头部,以便重用空间。

所以阶段二就是将记录从数据链表移除,加入到垃圾链表的头部。

也就是说,删除操作在事务提交前,只会经历阶段一,就是将记录的 delete_mask 标记为 1

此时接着执行一条删除的SQL语句,将id=2的这条数据删除:

BEGIN;
INSERT INTO account(id,card,balance) VALUES (1, 'AA', 0),(2, 'BB', 0);
DELETE FROM account WHERE id = 2;

因为是在同一个事务中,所以记录中的隐藏列trx_id没变,记录头中的delete_mask则标记为1了。然后生成了一个新的 undo log,并保存了记录中原本的trx_idroll_pointer,所以这个新的 undo log 就指向了旧的 undo log,而记录中的 roll_pointer 则指向这个新的 undo log。注意 undo log 中的事务编号也在递增。

update

在更新一条记录时,要把被更新的列的旧值记下来,这样之后回滚时再把这些列更新为旧值就好了。

Undo Log存储

undo log 分类

前边介绍了几种类型的 undo log,它们其实被分为两个大类来存储:

  • TRX_UNDO_INSERT

类型为 TRX_UNDO_INSERT_REC 的 undo log 属于此大类,一般由 INSERT 语句产生,或者在 UPDATE 更新主键的时候也会产生。

  • TRX_UNDO_UPDATE

除了类型为 TRX_UNDO_INSERT_REC 的 undo log,其他类型的 undo log 都属于这个大类,比如 TRX_UNDO_DEL_MARK_REC 、 TRX_UNDO_UPD_EXIST_REC ,一般由 DELETE、UPDATE 语句产生。

之所以要分成两个大类,是因为不同大类的 undo log 不能混着存储,因为类型为TRX_UNDO_INSERT_REC的 undo log 在事务提交后可以直接删除掉,而其他类型的 undo log 还需要提供MVCC功能,不能直接删除。

undo 页面链表

undo log 是存放在FIL_PAGE_UNDO_LOG类型的页中,一个事务中可能会产生很多 undo log,也许就需要申请多个undo页,所以 InnoDB 将其设计为一个链表的结构,将一个事务中的多个undo页连接起来。

但是前面说了 undo log 分为两大类,不能混着存储,所以如果事务中产生了这两大类型的 undo log,会创建两个链表,一个用来存储 TRX_UNDO_INSERT 类别的 undo log,一个用来存储 TRX_UNDO_UPDATE 类别的 undo log。

如果事务中还修改了临时表,InnoDB规定对普通表和临时表修改产生的 undo log 要分开存储,所以在一个事务中最多可能会有4个 undo 页面链表。

需要注意的是这些链表并不是事务一开始就分配好的,而是在需要某个类型的链表的时候才会去分配。

回滚段

redo log 是存放在重做日志文件中的,而 undo log 默认是存放在系统表空间中的一个特殊段(segment)中,这个段称为回滚段(Rollback Segment),链表中的页面都是从这个回滚段里边申请的。

InnoDB定义了128个回滚段(Rollback Segment),也就有128 Rollback Segment Header,每个Rollback Segment Header页面都对应着一个回滚段。一个 Rollback Segment Header 页面中包含1024undo slot,每个 undo slot 存放了 undo 链表头部的 undo 页的页号。就有128*1024=131072undo slot,也就是说最多同时支持131072个并发事务执行。

在系统表空间的第5号页面中存储了这128Rollback Segment Header页面地址。

事务回滚

前面在一个事务中增删改产生的一系列 undo log,都有 undo no 编号的。在回滚的时候,就可以应用这个事务中的 undo log,根据 undo no 从大到小开始进行撤销操作,就将数据还原为原来的样子了。

但需要注意的是,undo log 是逻辑日志,只是将数据库逻辑地恢复到原来的样子。所有修改都被逻辑地取消了,但是数据结构和页本身在回滚之后可能大不相同。因为同时可能很多并发事务在对数据库进行修改,因此不能将一个页回滚到事务开始的样子,因为这样会影响其他事务正在进行的工作。

MVCC

另一篇博客

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-11 00:40:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-11 00:40:02       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-11 00:40:02       58 阅读
  4. Python语言-面向对象

    2024-07-11 00:40:02       69 阅读

热门阅读

  1. 学习测试练习题

    2024-07-11 00:40:02       24 阅读
  2. QT log日志

    2024-07-11 00:40:02       29 阅读
  3. Angular页面项目以HTTPS方式启动调试

    2024-07-11 00:40:02       22 阅读
  4. ArduPilot开源飞控之AP_VisualOdom

    2024-07-11 00:40:02       20 阅读
  5. 如何实现跨域

    2024-07-11 00:40:02       19 阅读
  6. centos7yum-mysql-community-server安装流程步骤

    2024-07-11 00:40:02       24 阅读
  7. toFixed 四舍五入问题

    2024-07-11 00:40:02       21 阅读
  8. [C++][CMake][嵌套的CMake]详细讲解

    2024-07-11 00:40:02       19 阅读
  9. 65.指针函数和函数指针

    2024-07-11 00:40:02       24 阅读
  10. 网络安全测评技术与标准

    2024-07-11 00:40:02       25 阅读
  11. Qt之多线程编程(QThread)

    2024-07-11 00:40:02       25 阅读
  12. MySQL:left join 后用 on 还是 where?

    2024-07-11 00:40:02       21 阅读
  13. 最短路径算法(算法篇)

    2024-07-11 00:40:02       24 阅读
  14. 【数学建模】生产企业原材料的订购与运输

    2024-07-11 00:40:02       22 阅读