前置知识:

[1] MySQL物理数据行

为什么需要undo log?


undo log 的是在事务回滚的时候使用的。当事务进行到一半还没有提交的时候,内存中的数据已经发生了改变,这时候需要回滚的话,就需要对已经发生改变的数据进行恢复。这时候用来进行恢复数据的日志,就是 undo log 日志。

undo log的内容


执行事务的时候,除了写入 redo log 以外,还会写入 undo log 。undo log 的内容与增删改操作一一对应。例如,如果事务增加了一条记录,那么就会有一条删除这条新增对应记录的 undo log;如果是删除了一条记录,那就就会有一条增加记录的 undo log ; 如果是修改了一条记录,undo log 就是将对应数据的值修改回原来的值。

详细看一下 INSERT 语句对应的 undo log 的结构:

undo log
日志开始位置
主键
<列长度,列值>
表id undo log
日志编号
undo log
日志类型
undo log
日志结束位置
  • 日志开始位置:记录日志的开始位置。
  • 主键,列长度,列值:记录这行数据的对应的主键信息,如果有是联合主键也会将各个列的长度和值记录下来。如果没有主键,也会分配一个row_id 作为主键。
  • 表id:数据表的id。
  • 日志编号:每个日志都会有自己的编号。
  • 日志类型:INSERT 对应的日志类型是 TRX_UNDO_INSERT_REC 。
  • 日志结束位置:记录日志的结束位置。

UPDATE 语句和 DELETE 语句对应的日志也差不多。

undo log的版本链


每条数据的物理存储数据行里面,都会有2个附加的字段信息。DB_TRX_ID 事务 id,和 DB_ROLL_PTR 回滚指针。事务 id 是当前更新这条数据的事务的 id,回滚指针是指向当前事务之前生成的 undo log。这两个字段的结构和使用,用一个实际的例子来表达:

首先是用事务A(id=10)将值A插入数据库,这时候,DB_TRX_ID 就是事务A的 id,DB_ROLL_PTR 为空,因为当前是插入数据,所以这条数据没有上一个 undo log 日志, 如下:

image.png

这时候,产生了一个事务B(id=11) ,将这行数据的值改成了B 。DB_TRX_ID 就是事务B的id 11,DB_ROLL_PTR 指向上一个值A的 undo log ,如下图:

image.png

这时候,产生了一个事务C(id=12) ,将这行数据的值改成了C 。DB_TRX_ID 就是事务C的id 12,DB_ROLL_PTR 指向上一个值B的 undo log ,如下图:

image.png
从整个 redo log 生成的过程,可以通过 DB_ROLL_PTR 将每个事务修改过的值串联起来,形成一个链,这个就是 undo log 版本链。在这个版本链中,可以根据最后的 undo log 一直找到这个值从插入到当前的各个版本记录。