聊聊undo log
什么是undo log
undo log(回滚事务),在事务没有提交前,MySQL将记录更新操作的反向操作到undo log日志中,以便进行回退保证事务的原子性
undo log的作用
1.提供回滚操作
我们在进行数据更新操作的时候,不仅会记录redo log,还会记录undo log,如果因为某些原因导致事务回滚,那么这个时候MySQL就要执行回滚(rollback)操作,利用undo log将数据恢复到事务开始之前的状态。
2、提供多版本控制(MVCC)
在InnoDB
中MVCC
的实现是通过undo log
来完成。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo log
读取之前的行版本信息,以此实现非锁定读取。
undo的存储结构
1.回滚段和undo页
innodb对undo log采用段的方式进行管理,每个回滚段记录1024个undo log segment,在每个undo log segment进行undo页的申请
2.回滚段和事务
1.每一个事务只能有一个回滚段,一个回滚段可以同时服务于多个事务
2.当事务提交时,innodb会做两件事:
- 将
undo log
放入列表中,以供之后的purge
操作; - 判断
undo log
所在的页是否可以重用,若可以分配给下个事务使用。
回滚段中的数据分类
1、未提交的回滚数据:该回滚数据关联的事务尚未提交,要用于实现MVCC
,所以不能被删除和覆盖;
2、已提交但未过期的回滚数据:该回滚数据关联的事务已提交,但仍然受到undo retention
参数的影响继续保留;
3、事务已提交并过期的数据:该回滚数据属于过期数据,当回滚段满之后,会被优先覆盖掉。
undo log的类型
在InnoDB
中,undo log
分为两种:
insert undo log
:是指在insert
操作中产生的undo log
。因为insert
操作的记录,只对当前事务本身可见,对其他事务不可见(这是事务隔离性的要求),因此这种undo log
可以在事务提交后直接删除。不需要进行purge
操作。undate undo log
:是对delete
和update
操作产生的undo log
。该undo log
可能需要提供MVCC
机制使用,因此不能在事务提交时就进行删除,提交时放入undo log链表
,等待purge
线程进行最后的删除。
详细生成过程
对于InnoDB
来说,每条记录不仅包括了自身的数据,还包含了几个隐藏列:
DB_ROW_ID
:InnoDB
为没有主键和唯一索引的表自动添加的隐藏主键;DB_TRX_ID
:更改当前记录的事务id
;DB_ROLL_PTR
:回滚指针,指向undo log
的指针。
新增操作的undo log
start transaction;
insert into user(name) values('Tom');
commit;
此时行记录deletemark
标记为0,表示该记录并未删除,回滚指针指向了回滚编号为0的回滚日志,回滚日志记录了主键信息,说明若要回滚操作可以通过执行delete
这个主键实现。
不更新主键的undo log
start transaction;
update user set name = 'Sun' where id = 1;
commit;
此时执行了更新操作,并且更新的字段不是主键。此时记录的回滚指针指向了新生成的回滚编号为1的undo log
,编号为0的undo log
连接在编号为1的后面,当年记录回滚时也是先通过编号1的undo log
恢复到name
为'Tom'
,再通过编号0的undo log
删除记录。
更新主键的undo log
start transaction;
update user set id = 2 where id = 1;
commit;
对于更新主键的操作,会先把原来的数据deletemark
标识标记为1,这时并没有真正的删除数据,真正的删除会交给purge
清理线程去判断,然后在后面插入一条新的记录,新的记录也会产生undo log
,并且undo log
的序号会递增。
此时的事务如何回滚?
- 通过
undo no=3
的日志把id=2
的数据删除; - 通过
undo no=2
的日志把id=1
的数据的deletemark
还原成0;
- 通过undo no=1的日志把
id=1
的数据的name
还原成Tom
; - 通过
undo no=0
的日志把id=1的数据删除。
删除操作的undo log
记录的删除操作分为两个阶段:
- 将记录的
deletemark
标示位设置为1,其他的不做修改(实际会修改记录的trx_id
,roll_pointer
等隐藏列的信息)。 - 当该删除语句所在的事务提交之后,
undo purge
线程来真正的把记录删除掉。就是把记录从正常记录链表移除,加入到垃圾连表中。
删除操的undo log
只需要考虑对删除操作在阶段1所做的影响进行回滚,需要把该记录的trx_id
和roll_pointer
的隐藏列旧值都记到对应的undo log
中的trx_id
和roll_pointer
属性中。可以通过删除操作的undo log
的roll_pointer
的属性找到上一次对该记录改动产生的undo log
,以此来实现回滚。
purage线程的作用
清理undo
页和清除page
里带有Delete_Bit
标识的记录。在InnoDB
中,事务中的delete
操作并不会立刻将数据删除,而是先进行Delete Mark
标记,给记录标识上Delete_bit
,真正的清除工作是由purge
线程在后台完成的