暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

InnoDB 磁盘结构:undo 表空间

我与风来 2021-05-26
2719

> 本文翻译自 MySQL 官方文档。在陆陆续续阅读官方文档的过程中,已积累了不少经验,这篇文章也不像往篇那样晦涩难懂。加油!

undo(撤销) 表空间包含撤销日志,这些日志是记录的集合,其中包含有关如何撤销事务对聚集索引记录的最新更改的信息

这是 Undo 日志的作用,注意区分它与 Redo 的区别。

撤销表空间将按以下主题进行描述:

  • 默认撤销表空间

  • 撤销表空间大小

  • 新增撤销表空间

  • 删除撤销表空间

  • 移动撤销表空间

  • 配置回滚段数

  • 截断撤销表空间

  • 撤销表空间状态变量

默认撤销表空间

初始化 MySQL 实例时会创建两个默认的undo表空间。默认的undo表空间是在初始化时创建的,以便为回滚段提供一个位置,回滚段必须存在才能接受SQL语句。至少需要两个undo表空间才能支持自动截断undo表空间。请参见文章后面截断 undo 表空间一节。

默认的undo表空间是在 innodb_undo_directory
变量定义的位置创建的。如果该变量未定义,则会在数据目录中创建默认的undo表空间。默认的undo表空间数据文件名为 undo_001
undo_002
。在数据字典中定义的对应的undo表空间名称是 innodb_undo_001
innodb_undo_002

从MySQL 8.0.14开始,可以在运行时使用SQL创建额外的undo表空间。请参见添加撤消表空间一节。

撤销表空间大小

在MySQL 8.0.23之前,undo表空间的初始大小取决于 innodb_page_size
值。对于默认的16KB页面大小,初始撤消表空间文件大小为10MiB。对于4KB、8KB、32KB和64KB页大小,初始撤消表空间文件的大小分别为7MiB、8MiB、20MiB和40MiB。从MySQL 8.0.23开始,初始undo表空间的大小通常是16MiB。当通过截断操作创建新的撤消表空间时,初始大小可能不同。在这种情况下,如果文件扩大的大小大于了 16MB,并且上一次文件扩展发生在最近一秒钟内,则将以 innodb_max_undo_log_size
变量定义的四分之一大小创建新的undo表空间。

在MySQL 8.0.23之前,undo表空间一次扩展四个区段(区段可看作页的分组)。从MySQL 8.0.23开始,undo表空间至少扩展16MB。为了处理急剧增长,如果前一个文件扩展发生的时间不到0.1秒,则文件扩展的大小将增加一倍。扩展大小可以加倍多次,最大可达256MB。如果上一个文件扩展出现的时间早于0.1秒,则扩展大小将减少一半,也可以多次出现,最小为16MB。如果为撤消表空间定义了 AUTOEXTEND_SIZE
选项,则将按 AUTOEXTEND_SIZE
设置和由上述逻辑确定的扩展大小中的较大值进行扩展。

有关Pages(页), Extents(区段), Segments(段)这些概念将在后续的 InnoDB 文件空间管理一文中解释。

AUTOEXTEND_SIZE
默认情况下,当 file-per-table
表空间或常规表空间需要额外的空间时,表空间按照以下规则递增扩展:

  • 如果表空间的大小小于一个区段,则每次扩展一页。

  • 如果表空间大于1个区段,但小于32个区段,则每次扩展一个区段。

  • 如果表空间的大小超过32个区段,则一次扩展4个区段。

从MySQL 8.0.23开始,可以通过指定 AUTOEXTEND_SIZE
选项来配置表空间的扩展量。配置更大的扩展大小可以帮助避免碎片并方便大量数据的摄取

新增撤销表空间

因为在长时间运行的事务期间,撤销日志可能变得很大,因此创建额外的撤销表空间可以帮助防止单个撤销表空间变得太大。从MySQL 8.0.14 开始,可以在运行时使用 CREATE UNDO TABLESPACE
语法创建额外的undo表空间。

CREATE UNDO TABLESPACE tablespace_name ADD DATAFILE 'file_name.ibu';

undo表空间文件名的扩展名必须是.ibu。在定义undo表空间文件名时,不允许指定相对路径。一个完全限定的路径是允许的,但是这个路径必须是 InnoDB 知道的。已知路径是由 innodb_directories
变量定义的路径。建议使用唯一的undo表空间文件名,以避免移动或克隆数据时可能发生的文件名冲突。

在启动时,扫描 innodb_directories
变量定义的目录来恢复表空间文件。(扫描还会遍历子目录。)无论 innodb_directories
变量是否显式定义,innodb_data_home_dir
innodb_undo_directory
datadir
变量定义的目录都会自动附加到 innodb_directories
的值后面。因此,undo表空间可以驻留在由这些变量定义的路径中。

如果undo表空间文件名没有包含路径,则在 innodb_undo_directory
变量定义的目录下创建undo表空间。如果该变量未定义,则在数据目录中创建undo表空间。

如果要查询 undo 表空间名称和路径,可以使用如下语句:

SELECT TABLESPACE_NAME, FILE_NAME FROM INFORMATION_SCHEMA.FILES
  WHERE FILE_TYPE LIKE 'UNDO LOG';

一个MySQL实例最多支持127个undo表空间,包括MySQL实例初始化时创建的两个默认的undo表空间

删除撤销表空间

从MySQL 8.0.14开始,使用 CREATE UNDO TABLESPACE
语法创建的undo表空间可以在运行时使用 DROP UNDO TABALESPACE
语法删除。
undo表空间必须是空的,才能被删除。要清空undo表空间,必须首先使用 ALTER UNDO TABLESPACE
语法将undo表空间标记为非活动,以便不再使用该表空间为新事务分配回滚段。

ALTER UNDO TABLESPACE tablespace_name SET INACTIVE;

在undo表空间被标记为非活动后,当前在undo表空间中使用回滚段的事务被允许完成,在这些事务完成之前开始的任何事务也被允许完成。事务完成后,清除系统释放undo表空间中的回滚段,undo表空间被截断到初始大小。(截断undo表空间时也使用相同的过程。参见截断Undo表空间。)一旦undo表空间为空,就可以删除它。

DROP UNDO TABLESPACE tablespace_name;

可以通过查询 INFORMATION_SCHEMA.INNODB_TABLESPACES
表来监视undo表空间的状态。

SELECT NAME, STATE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES
  WHERE NAME LIKE 'tablespace_name';

非活动状态表示新事务不再使用undo表空间中的回滚段。空状态表示undo表空间为空并且准备被删除,或者准备使用 ALTER UNDO TABLESPACE tablespace_name SET ACTIVE
语句再次激活。试图删除一个非空的undo表空间将返回一个错误。

初始化MySQL实例时默认创建的undo表空间(innodb_undo_001
innodb_undo_002
)不能被删除。但是,可以使用 ALTER UNDO TABLESPACE tablespace_name SET INACTIVE
语句使它们处于非活动状态。在默认的undo表空间成为非活动的之前,必须有一个undo表空间来取代它的位置。在任何时候,至少需要两个活动的撤消表空间来支持撤消表空间的自动截断

移动撤销表空间

使用 CREATE UNDO TABLESPACE
语法创建的Undo表空间可以在服务器脱机时移动到任何已知目录。这些目录及其子目录在启动时被扫描,以获得undo表空间文件。在启动时发现一个移动到这些目录中的undo表空间文件,并假定它是被移动的undo表空间。

初始化MySQL实例时默认创建的undo表空间(innodb_undo_001
innodb_undo_002
)必须位于innodb_undo_directory
变量定义的目录中。如果innodb_undo_directory
变量未定义,则默认的undo表空间位于数据目录中。如果在服务器离线时移动了默认的undo表空间,则服务器必须使用配置到新目录的 innodb_undo_directory
变量启动。
undo日志的I/O模式使undo表空间成为SSD(俗称固态硬盘)存储的理想选择。

配置回滚段数

innodb_rollback_segments
变量定义了分配给每个undo表空间和全局临时表空间的回滚段的数量。innodb_rollback_segments
变量可以在启动或服务器运行时配置。
innodb_rollback_segments
的默认设置是128,这也是最大值。有关回滚段支持的事务数量的信息,请参见后续文章 “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
为动态变量,默认值为 1073741824 字节(1024 MiB)。

innodb_undo_log_truncate
变量启用:

  1. 超过 innodb_max_undo_log_size
    设置的默认和用户定义的undo表空间被标记为截断。为截断而选择的undo表空间以循环的方式执行,以避免每次截断相同的undo表空间。

  2. 驻留在所选undo表空间中的回滚段是不活动的,这样它们就不会被分配给新的事务。允许当前使用回滚段的现有事务完成。

  3. 清除系统通过释放不再使用的撤销日志来清空回滚段。

  4. 释放undo表空间中的所有回滚段后,运行截断操作,并将undo表空间截断为初始大小。截断操作后的undo表空间的大小可能大于初始大小,因为在操作完成后被立即使用。innodb_undo_directory
    变量定义了默认undo表空间文件的位置。

  5. 回滚段被重新激活,以便将它们分配给新的事务。

手动截断

手动截断undo表空间至少需要三个活动的undo表空间。在任何时候都需要两个活动的undo表空间来支持启用自动截断的可能性。至少有三个undo表空间满足这个要求,同时允许手动使一个undo表空间脱机。
要手动启动undo表空间的截断,可以通过发出以下语句来禁用undo表空间:

ALTER UNDO TABLESPACE tablespace_name SET INACTIVE;

在undo表空间被标记为非活动后,当前在undo表空间中使用回滚段的事务被允许完成,在这些事务完成之前开始的任何事务也被允许完成。事务完成后,清除系统释放undo表空间中的回滚段,undo表空间被截断到初始大小,undo表空间状态从非活动变为空。

ALTER UNDO TABLESPACE tablespace_name SET INACTIVE
语句使UNDO表空间失效时,清除线程将在下一次机会查找该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 表空间也能被删除。

加快撤销表空间的自动截断

清除线程负责清空和截断undo表空间。默认情况下,清除线程每调用128次就会查找一次撤销表空间来截断。清除线程查找要截断的undo表空间的频率由innodb_purge_rseg_truncate_frequency
变量控制,该变量的默认设置为128。

要增加频率,请减少 innodb_purge_rseg_truncate_frequency
设置。例如,要让清除线程在调用清除时每32次查找一次undo表空间,可以将 innodb_purge_rseg_truncate_frequency
设置为32。

SET GLOBAL innodb_purge_rseg_truncate_frequency=32;

截断Undo表空间文件对性能的影响

当undo表空间被截断时,undo表空间中的回滚段将被取消激活。其他undo表空间中的活动回滚段承担了整个系统负载的责任,这可能会导致轻微的性能下降。性能受影响的程度取决于以下几个因素:

  • undo表空间的个数

  • undo日志数量

  • undo表空间大小

  • I/O子系统的速度

  • 现有的长时间运行事务

  • 系统负载

避免潜在性能影响的最简单方法是增加undo表空间的数量。

监控 undo表空间截断

从MySQL 8.0.16开始,提供了undo和purge子系统计数器来监视与undo日志截断相关的后台活动。计数器名称和描述请查询 INFORMATION_SCHEMA.INNODB_METRICS
表。

SELECT NAME, SUBSYSTEM, COMMENT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME LIKE '%truncate%';

undo 表空间截断限制

从MySQL 8.0.21开始,检查点之间同一个undo表空间上截断操作的数量被限制为64。这个限制可以防止由于撤消表空间截断操作过多而导致的潜在问题,例如,在繁忙的系统上,如果 innodb_max_undo_log_size
设置得太低,就可能发生这种情况。如果超过了这个限制,那么仍然可以使undo表空间处于非活动状态,但是在下一个检查点之后它才会被截断。在MySQL 8.0.22中,这个限制从64提高到50000。

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%';
+----------------------------------+-------+
| Variable_name                    | Value |
+----------------------------------+-------+
| Innodb_undo_tablespaces_total    | 3     |
| Innodb_undo_tablespaces_implicit | 2     |
| Innodb_undo_tablespaces_explicit | 1     |
| Innodb_undo_tablespaces_active   | 3     |
+----------------------------------+-------+

我在前面创建undo表空间一节中创建了一个表空间,所以此处显示的计数为 1。

写在最后

官方文档的这一篇整个思路非常清晰,整篇读下来也不会有任何“卡顿”。文档倘若都能写成如此,那便很美了。


文章转载自我与风来,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论