undo log

undo log 是什么?

MySQL 事务 提到一个 MySQL 事务在未提交前其实已经执行,但仅仅只是对内存中缓冲页页修改,并未刷新回磁盘,事务提价后也不会立刻刷回磁盘,脏页刷盘时机可见 脏页刷新回磁盘

一个事务提交后不立刻刷盘,不怕数据丢失吗?这个由 redo log 保证持久性!!本篇文章讨论另外一个问题:事务如何回滚

如果一个事务由于客观原因 (如:断电,故障) 导致没有完全执行完,也就是事务没有执行到提交那一步,那么就应该回滚到执行事务之前的状态,保证事务的 原子性

如果一个事务由于主观原因 (如:手动回滚) 导致没有完全执行完,也需要回滚到执行事务之前的状态

回滚的实现思路也很简单:将事务中执行增删改操作的有用信息记录下来,回滚时执行相反操作即可

这些保存的内容就被称为 undo log (撤销日志),根据 undo log 可以恢复到事务执行前的状态

注意:insert undo log 只用于事务未提交前的回滚操作,所以如果事务已经提交,那么 undo log 就没用了,占用的空间会被系统回收,也可能会被重用

注意:update undo log 不仅用于回滚操作,而且在 MVCC 中也会使用,所以就算事务提交了也不能立马删掉 update undo log (包括 delete undo log)

trx_id & roll_pointer

InnoDB 行格式 中介绍一条记录中包含了三个隐藏列,其中 row_id 非必需,后面两个隐藏列就是本部分重点介绍的内容

11

trx_id 表示事务 ID,占 6 个字节,保存着记录最新被哪个事务修改 (包括插入、删除、更新操作)

roll_pointer 表示回滚指针,指向记录最新的 undo log,而删除更新的 undo log 中也有 roll_pointer 指针,会形成一个链表,表示该记录的版本链

undo log 格式

undo log 被存储到类型为 FIL_PAGE_UNDO_LOG 的页面中,每一个 undo log 都有一个编号 undo no,这个编号是以事务为范围,也就是每个事务所产生的 undo log 编号从 0 开始递增

存储 undo log 的页面在表空间中,而存储 redo log 的 block 不属于表空间,这是因为 redo log 为了保证持久化刷盘更频繁,独立于表空间外可以是顺序 IO,效率更高

由于存储 unod log 的页面上属于表空间,所以也存在数据丢失的风险,所以 undo log 的持久化和数据页一样都依靠 redo log 来保证!!

我们可以将 SQL 语句分为四类:增删改查

在正式介绍三种类型的 undo log 结构之前,建一个表:

插入操作对应的 undo log 结构

插入操作的回滚相比于另外两种来说简单很多,因为它只需要保存插入记录的主键,回滚时根据主键删除记录即可。TRX_UNDO_INSERT_REC 类型的 undo log 完整结构如下图所示:

18

现在向表中插入两条记录:

插入的两条记录对应的 undo log 如下:

19

下面来看看每条记录中 roll_pointer 指针:

20

删除操作对应的 undo log 结构

删除一个记录并非真正的删除了它,而只是将 deleted_flag 置为 1,并将记录添加到垃圾链表的头部

21

删除操作可以被分为两个阶段:

可以看到阶段 2 是在事务提交后才会执行,而事务提交后就不需要回滚,所以对于删除的回滚只关注阶段 1 即可,阶段 1 又被称为中间状态记录,如下图所示:

22

下面给出 TRX_UNDO_DEL_MARK_REC 类型的 undo log 完整结构,如下图所示:

23

将一条记录删掉,但事务未提交之前版本链如下:

24

更新操作对应的 undo log 结构

更新操作需要分情况讨论

下面给出 TRX_UNDO_UPD_EXIST_REC 类型的 undo log 完整结构,如下图所示:

25