15.6.3.4—Undo表空间

undo表空间包含undo日志。undo日志是记录集合,包含了可以撤销最近的对聚簇索引所做的修改所需的信息。

本章节将会介绍undo表空间的几个方面:

  • 默认undo表空间
  • undo表空间大小
  • 新增undo表空间
  • 删除undo表空间
  • 移动undo表空间
  • 设置回滚段数量
  • 清空undo表空间
  • undo表空间状态变量
清空 or 截断?

默认undo表空间 #

在MySQL实例初始化时,会创建两个默认的undo表空间。之所以在初始化时创建默认undo表空间,是为了给回滚段提供空间,而回滚段必须在执行SQL之前就存在。要支持undo表空间的自动清空,至少需要两个undo表空间。详见 清空undo表空间

默认undo表空间所在路径由参数innodb_undo_directory定义。如果这个参数没有配置,就会在数据目录下创建undo表空间。默认undo表空间的命名是undo_001和undo_002。在数据目录下时,相应的文件名是innodb_undo_001和innodb_undo_002。

从MySQL8.0.14开始,可以使用SQL在运行时创建更多的undo表空间。见 添加undo表空间。

undo表空间大小 #

在MySQL 8.0.23之前,undo表空间的初始大小取决于innodb_page_size的值。对于默认值16KB来说,undo表空间的初始大小就是10MB。对4KB,8恐怖,32KB,64KB的页大小,undo表空间的初始大小就是7MB,8MB,20MB,40MB。从MySQL8.0.23开始,undo表空间的初始大小是16MB。当truncate操作创建一个新的undo表空间时,初始大小可能有一点不同。在这种情形下,如果文件大小大于16MB,并且上一次扩容发生在最后一秒内,新创建的undo表空间大小是innodb_max_undo_log_size值的四分之一。

在MySQL8.0.23之前,一个undo表空间每次扩展四个区。从MySQL8.0.23开始,一个undo表空间每次扩展的最小值是16MB。为了处理快速增长,如果上一次扩容是在0.1秒内,会翻倍扩容(即增长100%)。翻倍扩容可以发生多次,但最大值是256MB。如果上一次扩容在0.1秒以前,扩容大小会减半,也可以发生多次,最小值是16MB。如果设置了选项AUTOEXTEND_SIZE,则取这个配置的值和上面介绍的逻辑生成的值的最大值。想了解更多关于 AUTOEXTEND_SIZE 选项的信息,见章节 15.6.3.9,“Tablespace AUTOEXTEND_SIZE Configuration”。

添加undo表空间 #

因为在发生长事务时,undo日志会变得很大,所以添加新的undo表空间可以阻止undo表空间变得更大。从MySQL8.0.14开始,可以通过CREATE UNDO TABLESPACE创建系统表空间。

create undo tablespace tablespace_name add datafile 'file_name.ibu';

undo表空间文件名后缀必须是.ibu。定义undo表空间文件时不允许用相对路径。允许使用完全路径,但是这个路径必须为InnoDB可知,意思是说,必须是由innodb_directories变量定义的。建议系统表空间文件名不要重复,以避免在迁移或复制数据时出现文件名冲突。 在一个复制环境中,源库和副本都必须有独立的undo表空间文件。在一个公共目录重放undo表空间文件的创建,可能会导致名称冲突。

在启动时,会扫描innodb_directries变量定义的目录查找undo表空间文件(这里的扫描也会遍历子目录)。不管innodb_directories是否显式定义,innodb_data_home_dir,innodb_undo_directory,以及datadir定义的目录都会被追加到innodb_directories中。所以,一个undo表空间可以在这些目录中的任意一个里。如果undo表空间文件不含路径,undo表空间就会被创建里变量innodb_undo_directory指定的目录里。如果这个变量没定义,就会创建在数据目录里。 InnoDB恢复过程要求undo表空间文件必须在已知目录里。undo表空间文件必须在其它数据文件被打开之前打开,这样才能允许回滚未提交的事务和数据字典便。一个在恢复之前不能被找到的undo表空间不能被使用,因为这可能会导致数据库的不一致性。如果在启动时发现已知目录下的undo表空间没有找到,会报告一个错误消息。已知目录要求也支持undo表空间的可移植性。见 移动undo表空间。

要在数据目录下创建undo表空间。将innodb_undo_directory变量设置为相对路径,并只在创建undo表空间时指定文件名。

要查看undo表空间名称和路径,查询information_schema.files:

select tablespace_name,file_name from information_schema.files where file_type like 'undo log';

一个MySQL实例支持最多127个undo表空间,这其中包含MySQL实例启动时创建的两个默认undo表空间。 在MySQL8.0.14之前,通过设置innodb_undo_tablespaces启动变量来创建额外的undo表空间。从MySQL8.0.14开始,这个变量被废弃,不可设置。 在MySQL8.0.14之前,增加innodb_undo_tablespaces的值,会创建指定数量的undo表空间,并将他们添加到活跃undo表空间列表中。减小innodb_undo_tablespaces的值,会将其从活跃undo表空间列表移除。 从活跃列表移除的undo表空间依然保持活跃,直到没有已有事务使用它们。innodb_undo_tablespaces可以在运行时使用set语句设置,也可以通过配置文件设置。 在MySQL8.0.14以前,不活跃的undo表空间不能被移除。可以在慢关闭之后手动移除,但是,不推荐。因为如果在服务关闭时,仍然有活跃的事务,在重启后,不活跃的undo表空间可能仍然包含活跃的undo日志。 从MySQL8.0.14开始,可以通过drop undo tablespace语法删除undo表空间。见 删除undo表空间

删除undo表空间 #

从MySQL8.0.14开始,使用create undo tablespace语法创建的undo表空间可以在运行时通过drop undo tablespace语法删除。

在一个undo表空间被删除之前,必须保证它是空的。要清空一个undo表空间,必须先使用alter undo tablespace语法讲这个undo表空间标记为非活跃,这样,这个undo表空间就不会被用于给新事务分配回滚段。

alter undo tablespace tablespace_name set inactive;

在一个undo表空间被标记为非活跃之后,回滚段在这个表空间中的事务仍然可以结束,就像之前已经结束的事务一样。在事务结束之后,清除系统会释放undo表空间中的回滚段。并且讲undo表空间重置为初始大小。见 清空undo表空间 一旦undo表空间空了,它就可以被删除了。

drop undo tablespace tablespace_name;
或许,也可以让这个undo表空间保持空的状态,以后有需要时再使用alter undo tablespace tablespace_name set active语句来激活。

一个undo表空间的状态,可以通过查询infomation schema中的innodb_tablespace表来监控。

select name,state from information_schema.innodb_tablespaces where name like 'tablespace_name';

状态为不活跃表明没有新事物在使用undo表空间中回滚段。状态为empty表明系统表空间已经被清空,可以删除了,或者可以重新被修改为活跃状态。删除一个不是empty状态的undo表空间,会报错。

默认undo表空间(innodb_undo_001和innodb_undo_002)不能删除。然而,却可以通过alter undo log tablespace tablespace_name set active将其进行删除。在将一个默认undo表空间修改过为不活跃状态之前,必须有一个新的undo表空间来替代它。无论什么时间都至少需要2个undo表空间,以支持undo表空间的删除。

移动undo表空间 #

使用create undo tablespaces语法创建的undo表空间可以在服务不在线时被移动到任何已知目录。已知目录指由innodb_directries变量定义的目录。innodb_data_home_dir,innodb_undo_directory,以及datadir定义的目录,都会被自动追加到innodb_directries里,不管它是否被显式定义。在服务启动时会扫描这些目录,查找undo表空间文件。在启动时被移动这些目录中任意一个目录下的undo表空间文件都会被认为是已经移动了。 MySQL实例启动时创建的默认undo表空间文件(innodb_undo_001和innodb_unod_002)必须在innodb_undo_directory定义的目录内。如果没有定义,就会在数据目录下创建。如果是在服务器离线时移动的undo表空间,启动服务时必须指定一个新的innodb_undo_directory目录。 undo日志的I/O模式使得其非常适合SSD存储设备。

设置回滚段的数量 #

innodb_rollback_segments变量定义了每个undo表空间和全局临时表空间中的回滚段数量。这个变量可以再启动时设置,也可以在服务运行时设置。 innodb_rollback_segments的默认值是128,最大值也是128。想了解一个回滚段支持多少事务的信息,请查阅章节 15.6.6 “Undo Logs”。

清空undo表空间 #

有两种方法来清空undo表空间。这两种访客可以单独使用,也可以混合使用,来管理undo表空间大小。一个方法是使用配置变量,是自动的。另一个方法是手动,通过执行sql语句进行。 自动方法不需要监控undo表空间大小。一旦启用了,它就会自动进行去活,截断,以及重新激活,不需要人工干预。如果你想何时使undo表空间离线的话,手动方法可能更合适。例如,你可能不想在负载高峰期进行这个操作。

自动 #

自动清空undo表空间需要至少两个活跃的undo表空间,因此这才能保证在一个undo表空间因为要被清空而脱机时,还有一个undo表空间是活跃的。当MySQL实例初始化时,默认会创建两个undo表空间。

想要自动清空undo表空间,需要先启用innodb_undo_log_truncate变量。例如:

mysql> set global innodb_undo_log_truncate=ON;

当innodb_undo_log_truncate这个变量被启用,大小超过innodb_max_undo_log_size这个变量的值的undo表空间就会被清空。innodb_max_undo_log_size这个变量是动态的。默认值是1024MB。

mysql>select @@innodb_max_undo_log_size;

当这个变量被启用后:

  1. 默认undo表空间和用户自定义表空间,都会为了清空而被标记。undo表空间的选择是以循环的方式进行,这样可避免每次都选中同一个。
  2. 被选中的undo表空间内的回滚段变为不活跃状态,这样它们就不会被分配给新的事物。允许正在使用这些回滚段的事务一直使用到结束。
  3. 清除系统通过释放不再使用的undo日志里清理回滚段。
  4. 在所有的的回滚段都被释放之后,执行清空操作,undo表空间被重置为初始大小。 清空操作执行完成之后,undo表空间的大小可能会比初始大小略大一些,因为可能在在完成之后立即被使用了。

innodb_undo_directory变量定义了默认undo表空间的存储位置。如果没有定义这个变量,默认的undo表空间将会被创建在数据目录下。所有的undo表空间文件,包括用户自定义的,都可以通过查询infomation schema中的files表看到:

select tablespace_name,file_name from information_schema.files where file_type like 'undo_log';
  1. 所有的回滚段都会变为不活跃状态,这样它们就可以被分配给新事务了。

手动 #

undo表空间的手动清理需要至少3个。2个undo表空间必须一直处于活跃状态,因为要支持可能开启的自动清理。三个的最低要求满足了在自动清理的同时,允许对一个undo表空间进行手动清理。 要手动初始化对一个undo表空间的清理过程,通过下面的语句将undo表空间变为不活跃状态:

alter undo tablespace tablespace_name set inactive;

在这个undo表空间被标记为不活跃之后,最近使用这个undo表空间中的回滚段的时候可以继续使用直到结束。在事务结束之后,清理西戎就会释放回滚段,undo表空间会被清空,重置到初始大小。然后undo表空间的状态就会从inactive变为empty。 当时用alter undo tablespace tablespace_name set inactive语句将一个undo表空间变为不活跃状态后,清理线程会在下一次查中查找这个undo表空间。一旦查找到并将其标记,清理线程返回的频率就会增加,以快速清空和截断这个undo表空间。

要检查一个undo表空间的状态,可以查阅information schema库的innodb_tablespaces表。

select name, state from information_schema.innodb_tablespaces where name like 'tablespace_name';

一旦undo表空间变为empty状态,就可以使用下面的语句将其重新激活:

alter undo tablespace tablespace_name set active;

一个处于empty状态的undo表空间也可以被删除。见 Dropping Undo Tablespaces.

加快对undo表空间的自动清空 #

清理线程负责清空和截断undo表空间。默认情况下,清理线程每被触发128次,就执行一次查找待清理undo表空间的操作。这个频率被变量innodb_purge_rseg_truncate_frequeency控制,默认值是128.

mysql>select @@innodb_purge_rseg_truncate_frequency;

要增加频率,可以将这个变量值调小。例如我们要改为每触发32次就执行一次,就将这个变量值改为32.

mysql>set global innodb_purge_rseg_truncate_frequency=32;

清空undo表空间文件的性能影响 #

当一个undo表空间被清理后,其中的回滚段变为不活跃状态。其它undo表空间的活跃回滚段就承担了整个系统的负载,这可能会造成性能的轻微下降。性能被影响的程度取决于几个因素:

  • undo表空间的数量
  • undo日志的数量
  • undo表空间的大小
  • I/O子系统的速度
  • 存在的长事务
  • 系统负载 避免潜在的性能影响的最简单方法是增加undo表空间的数量。

监控undo表空间的清空 #

从MySQL8.0.16开始 ,提供了用于监控与undo log清理相关的后台活动的undo和清理子系统计数器。要查阅计数器名称和描述,可以查询information schema 库的innodb_metrics表。

select name,subsystem,comment from information_schema.innodb_metrics where name like '%truncate%';

想了解关于开启计数器和查询计数器数据的信息,请查阅章节 15.15.6,“InnoDB INFORMATION_SCHEMA Metrics Table”.

清空undo表空间的限制 #

从MySQL8.0.21开始,在两个checkpoint之间,对同一个undo表空间进行的清空操作次数被限制在64.这个限制阻止了过度进行的清空操作(当innodb_max_undo_log_size设置的过小时就可能发生)。当超出限制后,undo表空间仍然会被标记为不活跃,但直到下一次checkpoint才进行清空操作。在MySQL8.0.22中,这个上限被从64提高到了50,000。

undo表空间清空恢复 #

一个undo表空间清空操作会在服务的日志目录下创建一个临时的undo_space_number_trunc.log文件。这个日志服务由innodb_log_group_home_dir定义。如果在执行清理过程中系统发生故障,这个临时日志文件可以让启动进程识别出哪个undo表空间正在被清空,然后继续操作。

undo表空间状态变量 #

下面的状态变量允许追踪undo表空间的总数量,非显式(innodb-创建)的undo表空间,显式的(用户创建的)undo表空间,以及活跃的undo表空间数量:

mysql>show status like 'innodb_undo_tablespaces %';

想了解关于状态变量的描述,请查阅章节5.1.10, “Server Status Variables”。