目录
[1. 什么是撤销日志](#1. 什么是撤销日志)
[2. 撤销日志的写入时机](#2. 撤销日志的写入时机)
[3. 撤销日志在撤销表空间中的组织形式是怎样的](#3. 撤销日志在撤销表空间中的组织形式是怎样的)
[4. 撤销日志的格式是怎样的 (结构)](#4. 撤销日志的格式是怎样的 (结构))
[5. 同操作对应的撤销日志如何区分](#5. 同操作对应的撤销日志如何区分)
[6. 撤销日志是如何组织在一起的](#6. 撤销日志是如何组织在一起的)
[7. 撤销日志如何分类](#7. 撤销日志如何分类)
[8. 如何理解 undo 链](#8. 如何理解 undo 链)
[9. 撤销日志为什么需要落盘](#9. 撤销日志为什么需要落盘)
[10. 撤销日志在内存中如何记录](#10. 撤销日志在内存中如何记录)
[11. 撤销日志的写入过程](#11. 撤销日志的写入过程)
[12. 撤销日志的回滚过程是怎样的](#12. 撤销日志的回滚过程是怎样的)
[13. 撤销日志的清理过程是怎样的](#13. 撤销日志的清理过程是怎样的)
1. 什么是撤销日志
当事务对数据进行修改的时候,每个修改操作都会在磁盘上创建与之对应的UndoLog当事务需要回滚时,会根据UndoLog逐一进行撤销操作,从而保证事务的原子性。也就是说撤销日志是为事务的回滚操作而诞生的机制,它是一个撤销操作记录的集合。
Undo日志保存在Undo日志段中,Undo日志段位于回滚段中,回滚段位于undo表空间和全局临时表空间中。
2. 撤销日志的写入时机
事务执行每个DML之前,会根据DML构建对应的撤销日志,并申请一个undologsegments (撤销日志段),把日志记录在申请到的撤销段中,再执行真正的DML操作
修改数据行之前记录撤销日志还是修改之后记录撤销日志?
修改之前记录最安全, 因为我们需要的是修改之前的数据
3. 撤销日志在撤销表空间中的组织形式是怎样的
Undo日志保存在Undo日志段中,Undo日志段位于回滚段中,回滚段位于undo表空间和全局临时表空间中

Undo log segments(撤销日志段)也称为撤销段,一个撤销日志段可以保存多个事务的回滚日志,但在同一时间只能被一个活跃事务使用,对应的空间在事务提交或回滚后才可以被重用。
rollback segments(回滚段)中包含撤销日志段,通常位于undo表空间和全局临时表空间中,使用系统变量 Innodb_rollback_segments 可以定义分配给每个undo表空间和全局临时 表空间的回滚段的数量,默认值为128,取值范围是[1,128]
一个回滚段支持的事务数取决于回滚段中的undo slots(槽数)和每个事务所需的undo日志数,一个回滚段中的undo槽数可以根据InnoDB页面大小进行计算,公式是(InnoDBPage Size/16),比如默认情况下 InnoDB Page Size 大小为 16KB,那么一个回滚段就可以包含 1024 个 undo slot用来存储事务的撤销日志。
回滚段中还记录了 History List 的头节点 History List Base Node,以及回滚段的大小
通过系统变量innodb_rolLback_segments可以设置Undo表空间中的回滚段数量,最大值和默认值都是128
4. 撤销日志的格式是怎样的 (结构)

一条记录在 Undo Log 页中的 Undo Log 日志大体包含两部分:分别是记录了 Undo 类型、表 ID、上一条、下一条日志的偏移地址等在内的"基本信息",以及记录了不同操作和数据的"操作信息"
5. 同操作对应的撤销日志如何区分
undo类型有很多种,最常见的就是增、删、改,分别用 TRX_UNDO_INSERT_REC, TRX_UNDO_DEL_MARK_REC 和 TRX_UNDO_UPD_EXIST_REC 表示
新增(TRX_UNDO_INSERT_REC)时的Undo Log操作信息相对简单,只记录了主键值,主键长度等主键信息
删除(TRX_UNDO_DEL_MARK_REC)时除了记录主键信息之外,还记录了旧的事务ID,旧的ROLL_POINTER信息,月用来构建有序的Undo版本链还会记录一些被索引字段的信息
更新(TRX_UNDO_UPD_EXIST_REC)时较为复杂,如果不更新主键则和删除时类似,会记录主键信息、旧的事务ID、旧的ROLL_POINTER信息、被索引字段的信息和被更新的信息;如果更新了主键,则会记录两条Undo Log,一条删除的和一条新增的;
6. 撤销日志是如何组织在一起的




在这三个特有的头信息之外,其他空间都会用来记录Undo Log日志,如果某个事务很大,一个页尾File Trailer Undo Log页没有办法完整记录,就需要申请新的Undo Log页,然后通过UNDO PAGE HEADER 中链表引用信息链接到前一个页,后面的这些页只需要记录Undo Log并不需要记录Undo头信息。
这个由UndoLog构成的链表称做Undo链,在事务中会起到非常重要的作用。
事务提交后UndoLog是否就可以删除了?
不一定 根据当前修改的数据有没有上一个版本而定
这里强调一下,对于新增操作所记录的UndoLog日志,在事务提交之后就可以直接删除了,而删除和更新的UndoLog日志还需要服务事务的MVCC,所以并不能直接删除,而是加入到 hisotry List 中。因些InnoDB为了最大程度节省空间提升效率对UndoLog进行了分类
7. 撤销日志如何分类
undo Log分为两大类:一类只记录新增操作,事务提交后可以直接清除;另一类记录删除和更新操作,所以相应的回滚链也会被区分为2个:Insert Undo链和 Update Undo链(Delete+Update)
根据事务的操作按需要写入Undo日志,比如,在普通表和临时表执行INSERT、UPDATE和DELETE操作的事务需要会写入以上四种类型的撤销日志;只在普通表上执行INSERT操作的事务只需要---个撤消日志
对普通表执行操作的事务将从指定的系统表空间或undo表空间的回滚段分配undo日志。对临时表执行操作的事务从指定的临时表空间回滚段分配undo日志。
8. 如何理解 undo 链
Insert Undo链和Update Undo链采用了不同的组织方式
对于新增操作,Insert Undo链中的每个Undo Log都会对应一条新的数据行,这个数据行中用ROLL_POINTER 信息来关联Undo Log,有在回滚时就可以通过它找到需要回滚的Undo Log了
一条新增的数据行对应一条UNDO日志,他们之间是一对一的关系
对于删除和更新,Update Undo链中的每个Undo Log也都对应一个数据行,每次更新都会通过Undo Log中的ROLL_POINTER进行关联,从而每个数据行都会构成一个Undo Log版本链,回滚的时候就可以依序撤销,这个版本链在事务的MVCC中起到了非常重要的作用,用于解决事务的"隔离性"
1.一条Update Undo链包含多条Undo日志
2.Undo日志的条数与对数据行进行的Update次数相关
3.每个被修改的数据行都对应着自己的Update Undo链

9. 撤销日志为什么需要落盘
运行大事务时,InnoDB为了避免大事务提交时统一落盘操作带来的性能问题,允许在事务进行的过程中就进行落盘操作并记录对应的Undo Log,当发生崩溃恢复时,通过回放Undo Log把未提交的事务进行回滚;
如果一个事务已经提交,但还有其他事务需要访问版本链中对应的Undo Log,那么也需要把相应的撤销日志保存到hisotry List 中。
Undo Log落盘必须要在真实数据页落盘之前
10. 撤销日志在内存中如何记录
与数据页在内存中的保存方式相同,撤销日志在内存中也保存在BufferPool中,与磁盘中的UndoLog页结构一致,内存中每个UndoLog页通过控制块管理
在内存中使用四个链表来管理正在使用的UndoLog页和空间UndoLog页,根据不同的日志类型分为:
InsertList:正在被使用的用来管理Insert类型的UndoLog页
InsertCacheList:空闲的或被清理后可以被后续事务重用的Insert类型UndoLog页
UpdateList:正在被使用的用来管理Update类型的UndoLog页
UpdateCacheList:空闲的或被清理后可以被后续事务重用的Update类型UndoLog页
11. 撤销日志的写入过程
写事务开始时,会先分配一个处理 ACTIVE 状态的 RolLback Segment
第一次DML操作产生Undo Record时,会轮询当前 RolLback Segment 中可用的 Slot,以便获取---个 Undo Log Segment
根据撤销日志的类型获取Undo Log页,并挂载到对应的List当中
在UndoLog页顺序写入日志,当一个Undo Log页写满之后,会获取新的Undo Log页以便继续写入当前事务生成的日志,这里注意:单条Undo Log不能跨页存储,也就是说当某条日志在当前页中放不下时,会整体保存下一页中
由后台线程把日志刷入磁盘
当事务结束之后(commit或者rollback),insert 日志类型对应的Undo Log Segment 和Undo Log page 会直接回收,而update日志类型对应的Undo Log Segment和Undo Log page 会等待后台的清理操作完成后,确保日志不会有事务再访问后进行回收,回收的Undo Log页被挂载到CacheList中

12. 撤销日志的回滚过程是怎样的
回滚操作可以是用户通过rollback主动触发,也可能发生在崩溃恢复时,不论是哪种触发条件,回滚操作都是相同的,基本过程就是读取该事务的Undo Log,从后向前依次进行逆向操作,从而恢复索引记录
对于Insert类型的回滚操作就是Delete,在删除的过程中会重新调整主键索引和二级索引
对于Update和Delete类型的回滚操作,主要是回退这次操作在所有主键索引和二级索引的影响,可能包括重新插入被删除的二级索引记录、去除行管理信息中的Delete Mark标记、将非索引记录修改回之前的值等
完成回滚的Undo Log会进行回收,将不再使用的Undo Log页的磁盘空间还给 Undo Log Segment,这个过程是写入过程的逆操作
13. 撤销日志的清理过程是怎样的
InnoDB通过保存多份UndoLog的历史版本来实现MVCC,当某个历史版本已经确认不会被任何现有的和未来的事务访问时,就应该被清理掉
当开启一个事务时都会被分配一个事务编号 trx_id,而事务进行读操作时会创建一个ReadView,并记录当前所有事务中的最小活跃事务编号 m_Low_Limit_id,如果版本链中日志的trx_id < m_Low_Limit_id,则表示当前读操作发生时,日志对应的事务已提交,其修改的新版本是可见的,因此不再需要通过Undo版本链构建之前的版本,这个事务的UndoLog也就可以被清理了。
Undo的清理工作是由专门的后台线程进行扫描和分发,并由多个线程进行清理,并可以通过系统变量innodb_purge_threads配置清理线程数,系统变量innodb_purge_batch_size可以配置每次清理的页数
