【MySQL高阶】21.撤销表空间,撤销日志

【MySQL高阶】21.撤销表空间,撤销日志 文章目录6. InnoDB 磁盘文件6.6 撤销表空间 - Undo Tablespaces6.6.4 如何删除撤销表空间6.6.4.1 删除撤销表空间的示例6.6.4.2 撤销表空间被置为不活动并且已被截断为初始大小这时不想删除了是否可以重新启用6.6.5 如何查看撤销表空间的状态6.7 撤销日志 - Undo Log6.7.1 什么是撤销日志6.7.1.1 撤销日志的写入时机6.7.2 撤销日志在撤销表空间中的组织形式是怎样的6.7.3 撤销日志的格式是怎样的6.7.3.1 在事务中不同的DML操作对应的撤销日志是否不同6.7.3.2 不同操作对应的撤销日志如何区分6.7.4 撤销日志是如何组织在一起的6.7.4.1 如何理解Undo链以及它是如何构成的6.7.4.2 事务提交后Undo Log是否就可以删除了6.7.5 撤销日志如何分类6.7.6 InnoDB最大支持并发读写事务的数量如何计算6.7.7 如何理解Undo链6.7.8 撤销日志为什么需要落盘6.7.8.1 撤销日志在内存中如何记录6.7.8.2 撤销日志的写入过程是怎样的6.7.8.3 撤销日志的回滚过程是怎样的6.7.8.4 撤销日志的清理过程是怎样的6. InnoDB 磁盘文件6.6 撤销表空间 - Undo Tablespaces6.6.4 如何删除撤销表空间从MySQL 8.0.14开始使用CREATE UNDO TABLESPACE语法创建的撤销表空间可以使用DROP UNDO TABALESPAC语法删除撤销表空间在被删除之前必须是空的要清空撤销表空间必须首先使用ALTER UNDO TABLESPACE语法将撤销表空间标记为不活动以便该表空间不再用于其他新的事务也就是没有新的日志往里面写# 语法 ALTER UNDO TABLESPACE tablespace_name SET INACTIVE;在将undo表空间标记为非活动后等待当前undo表空间的事务完成后表空间被截断到初始大小当undo表空间为空就可以进行删除操作# 语法 DROP UNDO TABLESPACE tablespace_name;总结从MySQL 8.0.14开始使用CREATE UNDO TABLESPACE语法创建的撤销表空间可以使用DROP UNDO TABALESPAC语法删除但要确保撤销表空间在被删除之前必须是空的具体的操作步骤如下要删除的撤销表空间必须是通过create语句创建的将撤销表空间标记为不活动状态等待当前undo表空间日志对应的事务完成后也就是日志没用后等待撤销表空间中的日志被清空通过drop语句。执行删除操作6.6.4.1 删除撤销表空间的示例# 查询指定的表空间状态 mysql SELECT NAME, STATE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES - WHERE NAME LIKE tablespace_test; ------------------------- | NAME | STATE | ------------------------- | tablespace_test | active | # 活动状态 ------------------------- 1 row in set (0.00 sec) # 设置为不活动状态 mysql ALTER UNDO TABLESPACE tablespace_test SET INACTIVE; Query OK, 0 rows affected (0.00 sec) # 再次查询状态 mysql SELECT NAME, STATE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES WHERE NAME LIKE tablespace_test; ------------------------ | NAME | STATE | ------------------------ | tablespace_test | empty | # 表空间为empty已经情况也有可能是inactive 等待清空 ------------------------ 1 row in set (0.00 sec) # 当表空间状态为empty时要以进行删除操作 mysql DROP UNDO TABLESPACE tablespace_test; Query OK, 0 rows affected (0.01 sec) # 查询撤销表空间发现删除成功 mysql SELECT TABLESPACE_NAME, FILE_NAME FROM INFORMATION_SCHEMA.FILES WHERE FILE_TYPE LIKE UNDO LOG; ----------------------------- | TABLESPACE_NAME | FILE_NAME | ----------------------------- | innodb_undo_001 | ./undo_001 | | innodb_undo_002 | ./undo_002 | ----------------------------- 2 rows in set (0.00 sec) mysql6.6.4.2 撤销表空间被置为不活动并且已被截断为初始大小这时不想删除了是否可以重新启用undo表空间状态为空时可以重新激活方法如下# 语法 ALTER undo tablespace tablespace_name SET ACTIVE;6.6.5 如何查看撤销表空间的状态通过SHOW STATUS LIKE Innodb_undo_tablespaces%;语句可以查看撤销表空间的基本信息mysql SHOW STATUS LIKE Innodb_undo_tablespaces%; ----------------------------------------- | Variable_name | Value | ----------------------------------------- | Innodb_undo_tablespaces_total | 2 | # 撤销表空间的总数 | Innodb_undo_tablespaces_implicit | 2 | # 隐式(InnoDB)创建撤销表空间数量 | Innodb_undo_tablespaces_explicit | 0 | # 显式(用户)创建撤销表空间数量 | Innodb_undo_tablespaces_active | 2 | # 活动中的撤销表空间数量 ----------------------------------------- 4 rows in set (0.00 sec) mysql6.7 撤销日志 - Undo Log6.7.1 什么是撤销日志当事务对数据进行修改的时候每个修改操作都会在磁盘上创建与之对应的Undo Log当事务需要回滚时会根据Undo Log逐一进行撤销操作从而保证事务的原子性。也就是说撤销日志是为事务的回滚操作而诞生的机制它是一个撤销操作记录的集合。Undo日志保存在Undo日志段中Undo日志段位于回滚段中回滚段位于undo表空间撤销表空间和全局临时表空间中。官网描述回滚日志是与单个读写事务相关联的一组回滚日志记录。回滚日志记录包含有关如何撤销事务对聚簇索引记录的最新更改的信息。如果另一个事务需要在一致性读取操作中查看原始数据则会从回滚日志记录中检索未修改的数据。回滚日志存在于回滚日志段中回滚日志段包含在回滚段中。回滚段位于回滚表空间和全局临时表空间中。6.7.1.1 撤销日志的写入时机在事务执行每个DML之前会根据DML构建对应的撤销日志并申请一个undo log segments(撤销日志段)把日志记录在申请到的撤销段中再执行真正的DML操作执行过程如下所示6.7.2 撤销日志在撤销表空间中的组织形式是怎样的首先看来撤销日志在撤销表空间中的组织结构图如下所示Undo log segments(撤销日志段)也称为撤销段一个撤销日志段可以保存多个事务的回滚日志但在同一时间只能被一个活跃事务使用对应的空间在事务提交或回滚后才可以被重用。rollback segments(回滚段)中包含撤销日志段通常位于undo表空间和全局临时表空间中使用系统变量Innodb_rollback_segments可以定义分配给每个undo表空间和全局临时表空间的回滚段的数量默认值为128取值范围是[1, 128]一个回滚段支持的事务数取决于回滚段中的undo slots(槽数)和每个事务所需的undo日志数一个回滚段中的undo槽数可以根据InnoDB页面大小进行计算公式是(InnoDB Page Size / 16)比如默认情况下InnoDB Page Size大小为16KB那么一个回滚段就可以包含1024个undo slot用来存储事务的撤销日志。回滚段中还记录了History List的头节点History List Base Node以及回滚段的大小通过系统变量innodb_rollback_segments可以设置Undo表空间中的回滚段数量最大值和默认值都是128# 查看Undo表空间中的回滚段数量 mysql show variables like innodb_rollback_segments; --------------------------------- | Variable_name | Value | --------------------------------- | innodb_rollback_segments | 128 | --------------------------------- 1 row in set (0.00 sec) # 设置Undo表空间中的回滚段数量 mysql SET GLOBAL innodb_rollback_segments 128; Query OK, 0 rows affected (0.00 sec) mysql总结撤销表空间中包含rollback segments(回滚段)每个回滚段中包含若干undo slots(槽数)每个槽对应一个Undo log segments(撤销日志段)撤销日志段中包含具体的撤销日志。6.7.3 撤销日志的格式是怎样的撤销日志格式示意图如下一条记录在Undo Log页中的Undo Log日志大体包含两部分分别是记录了Undo类型、表ID、上一条、下一条日志的偏移地址等在内的基本信息以及记录了不同操作和数据的操作信息如上图所示6.7.3.1 在事务中不同的DML操作对应的撤销日志是否不同在执行DML语句操作数据库时不同SQL语句对应的撤销操作不同不同的撤销操作对应的Undo Log撤销日志存储格式也不相同。按照增、删、改等不同的DML操作生成对应的撤销日志。6.7.3.2 不同操作对应的撤销日志如何区分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.7.4 撤销日志是如何组织在一起的撤销日志的组织示意图如下一条条Undo Log会被逐一放在Undo Log页中Undo Log页和其他类型的页一样都会包含头尾信息除此之外还有Undo Log特有的信息包括UNDO PAGE HEADER记录了Undo Log页类型第一条日志偏移位置上一页下一页链表引用等信息UNDO LOG SEGMENT HEADER记录了回滚段信息标记当前这个段是不活跃的记录最后一个事务的偏移量记录段中第一个页的位置UNDO LOG HEADER每个事务都是以UNDO LOG HEADER开始作为事务开始的标记记录了产生这条日志的事务Id:Trx ID记录事务的编号记录事务第一条日志的偏移量事务的提交顺序Trx No其他事务相关信息在这三个特有的头信息之外其他空间都会用来记录Undo Log日志如果某个事务很大一个Undo Log页没有办法完整记录就需要申请新的Undo Log页然后通过UNDO PAGE HEADER中链表引用信息链接到前一个页后面的这些页只需要记录Undo Log并不需要记录Undo头信息。这个由Undo Log构成的链表称做Undo链在事务中会起到非常重要的作用。6.7.4.1 如何理解Undo链以及它是如何构成的本小节最后会详细介绍Undo链的构成6.7.4.2 事务提交后Undo Log是否就可以删除了这里强调一下对于新增操作所记录的Undo Log日志在事务提交之后就可以直接删除了而删除和更新的Undo Log日志还需要服务事务的MVCC所以并不能直接删除而是加入到hisotry list中。主要是根据当前修改的数据有没有上一个版本而定因些InnoDB为了最大程度节省空间提升效率对Undo Log进行了分类关于MVCC和hisotry list在事务和锁专题中详细介绍所有的事务都不访问的数据才会删除6.7.5 撤销日志如何分类Undo Log分为两大类一类只记录新增操作事务提交后可以直接清除另一类记录删除和更新操作所以相应的回滚链也会被区分为2个Insert Undo链 和Update Undo链(Delete Update)如是是INSERT操作这个页中保存UNDOLOG全都是关于INSERT操作的UNDO日志。如是是UPDATE或DELETE操作这个页中保存UNDO LOG全都是关于UPDATE或DELETE操作的UNDO日志。也就是说一个事务可能会申请不同类型的页。另外普通表和临时表分别对应这两类Undo链如是一个事务既有新增又有修改并且用到了临时表那么这个事务最多可以分配四个撤销日志也就是四个Undo链分别是对用户定义的普通表进行INSERT操作保存在系统或用户创建的撤销表空间中对用户定义的普通表进行UPDATE和DELETE操作保存在系统或用户创建的撤销表空间中对用户定义的临时表进行INSERT操作保存在临时表空间中临时表空间中有一个回滚段专用于保存UNDO日志对用户定义的临时表进行UPDATE和DELETE操作保存在临时表空间中临时表空间中有一个回滚段专用于保存UNDO日志根据事务的操作按需要写入Undo日志比如在普通表和临时表执行INSERT、UPDATE和DELETE操作的事务需要会写入以上四种类型的撤销日志。只在普通表上执行INSERT操作的事务只需要一个撤消日志。对普通表执行操作的事务将从指定的系统表空间或undo表空间的回滚段分配undo日志。对临时表执行操作的事务从指定的临时表空间回滚段分配undo日志。6.7.6 InnoDB最大支持并发读写事务的数量如何计算可以用以下公式计算InnoDB能够支持的并发读写事务的数量如果每个事务执行INSERT或UPDATE或DELETE操作注意只执行一种类型的操作并发读写事务数为名词解释innodb_page_size页大小innodb_rollback_segments回滚段的数量number of undo tablespaces撤销表空间的个数# 最大支持 (16384 / 16) * 128 * 127 16,646,144 (innodb_page_size / 16) * innodb_rollback_segments * number of undo tablespaces如果每个事务执行INSERT和UPDATE或DELETE操作并发读写事务数为:# 最大支持 (16384 / 16 / 2) * 128 * 127 8,323,072 (innodb_page_size / 16 / 2) * innodb_rollback_segments * number of undo tablespaces如果一个事务在临时表上执行INSERT操作并发读写事务数为:# 临时表最大支持 (16384 / 16) * 128 131,072 (innodb_page_size / 16) * innodb_rollback_segments如果一个事务在临时表上执行INSERT和UPDATE或DELETE操作并发读写事务数为:# 临时表最大支持 (16384 / 16 / 2) * 128 65,536 (innodb_page_size / 16 / 2) * innodb_rollback_segments6.7.7 如何理解Undo链Insert Undo链 和Update Undo链采用了不同的组织方式对于新增操作Insert Undo链中的每个Undo Log都会对应一条新的数据行这个数据行中用ROLL_POINTER信息来关联Undo Log在回滚时就可以通过它找到需要回滚的Undo Log了如图所示对于删除和更新Update Undo链中的每个Undo Log也都对应一个数据行每次更新都会通过Undo Log中的ROLL_POINTER进行关联从而每个数据行都会构成一个Undo Log版本链回滚的时候就可以依序撤销这个版本链在事务的MVCC中起到了非常重要的作用用于解决事务的隔离性相关内容在事务和锁专题中详细介绍。一条Update Undo链包含多条Undo日志Undo日志的条数与对数据行进行的Update次数相关每个被修改的数据行都对应着自己的Update Undo链Update Undo链如下图所示以下是一个关于更新操作的Undo链6.7.8 撤销日志为什么需要落盘在对数据进行修改时都是在内存中操作的也就是在Buffer Pool中修改对应的数据页在修改数据页之前先把对应的撤销日志记录在内存中如果此时事务回滚直接根据内存中的撤销日志做回滚操作即可在修改完成提交事务后脏页进行落盘操作此时事务已提交不能回滚所以撤销日志也就失效了当服务器崩溃时如果事务没有提交所有的修改都在内存中还没有落盘对于修改直接丢弃因为是不完整的事务如果事务已经提交则根据重做日志和双写缓冲区中的备份进行恢复通过分析看上去撤销日志并不需要落盘其实以上的分析场景并没有考虑到全部的场景比如大事务的运行一个事务需要运行很久、MVCC中版本链什么时候可以销毁、事务的不同隔离级别等因素总结在运行大事务时InnoDB为了避免大事务提交时统一落盘操作带来的性能问题允许在事务进行的过程中就进行落盘操作并记录对应的UndoLog当发生崩溃恢复时通过回放UndoLog把未提交的事务进行回滚Undo Log落盘必须在真实数据页落盘之前如果一个事务已经提交但还有其他事务需要访问版本链中对应的Undo Log那么也需要把相应的撤销日志保存到hisotry list中。不同隔离级别下没有提交的事务也可能会落盘回滚时依然要完成撤销操作6.7.8.1 撤销日志在内存中如何记录与数据页在内存中的保存方式相同撤销日志在内存中也保存在Buffer Pool中与磁盘中的Undo Log页结构一致内存中每个Undo Log页通过控制块管理在内存中使用四个链表来管理正在使用的UndoLog页和空闲UndoLog页根据不同的日志类型分为Insert List正在被使用的用来管理Insert类型的UndoLog页Insert Cache List空闲的或被清理后可以被后续事务重用的Insert类型UndoLog页Update List正在被使用的用来管理Update类型的UndoLog页Update Cache List空闲的或被清理后可以被后续事务重用的Update类型UndoLog页6.7.8.2 撤销日志的写入过程是怎样的当写事务开始时会先分配一个处理ACTIVE状态的Rollback Segment回滚段当第一次DML操作产生Undo Record时会轮询当前Rollback Segment中可用的Slot槽 以便获取一个Undo Log Segment撤销日志段 根据撤销日志的类型获取UndoLog页并挂载到对应的List当中在UndoLog页顺序写入日志当一个UndoLog页写满之后会获取新的UndoLog页以便继续写入当前事务生成的日志。这里注意单条UndoLog不能跨页存储也就是说当某条日志在当前页中放不下时会整体保存下一页中由后台线程把日志刷入磁盘当事务结束之后(commit或者rollback)insert日志类型对应的Undo Log Segment和UndoLog page会直接回收而update日志类型对应的Undo Log Segment 和UndoLog page会等待后台的清理操作完成后确保日志不会有事务再访问后进行回收回收的UndoLog页被挂载到Cache List缓存链表中。6.7.8.3 撤销日志的回滚过程是怎样的回滚操作可以是用户通过rollback主动触发也可能发生在崩溃恢复时不论是哪种触发条件回滚操作都是相同的基本过程就是读取该事务的Undo Log从后向前依次进行逆向操作从而恢复索引记录对于Insert类型的回滚操作就是Delete在删除的过程中会重新调整主键索引和二级索引对于Update和Delete类型的回滚操作主要是回退这次操作在所有主键索引和二级索引的影响可能包括重新插入被删除的二级索引记录、去除行管理信息中的Delete Mark标记、将主索引记录修改回之前的值等完成回滚的Undo Log会进行回收将不再使用的UndoLog页的磁盘空间还给Undo Log Segment这个过程是写入过程的逆操作。6.7.8.4 撤销日志的清理过程是怎样的InnoDB通过保存多份Undo Log的历史版本来实现MVCC当某个历史版本已经确认不会被任何现有的和未来的事务访问时就应该被清理掉当开启一个事务时都会被分配一个事务编号trx_id而事务进行读操作时会创建一个ReadView读视图/读快照并记录当前所有事务中的最小活跃事务编号m_low_limit_id如果版本链中日志的trx_id m_low_limit_id则表示当前读操作发生时日志对应的事务已提交其修改的新版本是可见的 因此不再需要通过Undo版本链构建之前的版本这个事务的Undo Log也就可以被清理了。Undo的清理工作是由专门的后台线程进行扫描和分发并由多个线程进行清理并可以通过系统变量innodb_purge_threads配置清理线程数系统变量innodb_purge_batch_size可以配置每次清理的页数这里不再进行过多的讨论。