15.6 InnoDB 磁盘(On-Disk)结构

文章目录

  • 15.6.1 表
    • 15.6.1.1 创建 InnoDB 表
      • 行格式
      • 主键
      • 查看 InnoDB表的属性
    • 15.6.1.2 在(数据目录)外部创建表
      • 使用 `DATA DIRECTORY` 子句
      • 使用 `CREATE TABLE ... TABLESPACE` 语法
      • 在外部通用表空间中创建表
    • 15.6.1.3 导入 InnoDB 表
      • 先决条件
      • 导入表
      • 导入分区表
      • 导入表分区
      • 限制
      • 使用说明
      • 内部信息
    • 15.6.1.4 移动或复制 InnoDB 表
      • 导入表
      • MySQL Enterprise Backup
      • 复制数据文件(冷备方式)
      • 从逻辑备份恢复
    • 15.6.1.5 将表从 MyISAM 转换为 InnoDB
    • 15.6.1.6 InnoDB 对自增(AUTO_INCREMENT)的处理
      • InnoDB 自增(AUTO_INCREMENT )锁模式
      • InnoDB 自增锁模式使用说明
      • InnoDB 自增计数器初始化
      • 说明
  • 15.6.2 索引
    • 15.6.2.1 聚集索引和次级索引
      • 聚集索引是如何加速内存的
      • 次级索引与聚集索引的关系
    • 15.6.2.2 InnoDB 索引的物理结构
    • 15.6.2.3 排序索引构建
      • 为将来的索引增长保留B树页面空间
      • 排序索引构建和全文索引支持
      • 排序索引构建和压缩表
      • 排序索引构建和重做日志记录
      • 排序索引构建和优化器统计信息
    • 15.6.2.4 InnoDB 全文(Full-Text)索引
      • InnoDB 全文索引设计
      • InnoDB 全文索引表
      • InnoDB 全文索引缓存
      • 全文索引的 DOC_ID 和 FTS_DOC_ID
      • InnoDB 全文索引删除处理
      • InnoDB 全文索引事务处理
      • 监控 InnoDB 全文索引
  • 15.6.3 表空间
    • 15.6.3.1 系统表空间
      • 调整系统表空间的大小
        • 增加系统表空间的大小
        • 减小 InnoDB 系统表空间大小
      • 为系统表空间使用原始磁盘分区(Raw Disk Partition)
        • 在 Linux 和 Unix 系统上分配原始磁盘分区
        • 在 Windows 上分配原始磁盘分区
    • 15.6.3.2 单表表空间
      • 单表表空间配置
      • 单表表空间数据文件
      • 单表表空间的优势
      • 单表表空间的缺点
    • 15.6.3.3 普通表空间
      • 普通表空间的功能
      • 创建普通表空间
      • 向普通表空间添加表
      • 普通表空间支持的行格式
      • 使用`ALTER TABLE`在表空间之间移动表
      • 重命名普通表空间
      • 删除普通表空间
      • 普通表空间限制
    • 15.6.3.4 撤销(UNDO)表空间
      • 默认 Undo 表空间
      • Undo 表空间大小
      • 添加 Undo 表空间
      • 删除 Undo 表空间
      • 移动 Undo 表空间
      • 配置回滚段的数量
      • 截断 Undo 表空间
        • 自动截断
        • 手动截断
        • 加快 Undo 表空间的自动截断
        • 截断 Undo 表空间文件的性能影响
        • 监控截断 Undo 表空间
        • Undo 表空间截断的限制
        • Undo 表空间截断的恢复
      • Undo 表空间状态变量
    • 15.6.3.5 临时表空间
      • 会话临时表空间
      • 全局临时表空间
    • 15.6.3.6 在服务器离线时移动表空间
      • 使用说明
    • 15.6.3.7 禁用表空间路径验证
    • 15.6.3.8 优化在 Linux 上的表空间分配
    • 15.6.3.9 表空间 AUTOEXTEND_SIZE 配置
  • 15.6.4 双写缓冲区(Doublewrite Buffer)
  • 15.6.5 重做日志(Redo Logs)
    • 配置重做日志容量(MySQL 8.0.30 及以后)
    • 配置重做日志容量(MySQL 8.0.30 以前)
    • 自动配置重做日志容量
    • 归档重做日志
      • 性能考虑因素
    • 禁用重做日志
    • 相关话题
  • 15.6.6 撤销日志(Undo Logs)

本章描述了 InnoDB 的磁盘结构和相关话题。

15.6.1 表

本节覆盖了有关 InnoDB 表的话题。

15.6.1.1 创建 InnoDB 表

使用CREATE TABLE语句创建 InnoDB 表。例如:

CREATE TABLE t1 (a INT, b CHAR (20), PRIMARY KEY (a)) ENGINE=InnoDB;

InnoDB 被定义为默认存储引擎时不需要 ENGINE=InnoDB 子句,这是默认情况。然而,如果要在不同的默认存储引擎不是 InnoDB 或为未知的 MySQL 服务器实例上重放CREATE TABLE语句,ENGINE子句则是有用的。可以通过如下语句确定 MySQL 服务器实例上的默认存储引擎:

mysql> SELECT @@default_storage_engine;
+--------------------------+
| @@default_storage_engine |
+--------------------------+
| InnoDB                   |
+--------------------------+

InnoDB 表默认创建于单表(file-per-table)表空间。要在系统表空间中创建 InnoDB 表,在创建该表前禁用 innodb_file_per_table 变量。要在通用表空间创建 InnoDB 表,使用 CREATE TABLE ... TABLESPACE 语法。更多信息查看 15.6.3 表空间 。

行格式

InnoDB 表的行格式决定它如何物理存储在磁盘上。InnoDB 支持4种行格式,每种拥有不同的存储特性。支持的行格式包括REDUNDANTCOMPACTDYNAMIC,和 COMPRESSED,默认行格式为 DYNAMIC 。有关行格式特征的信息,参考 15.10 InnoDB 行格式 。

innodb_default_row_format 变量定义默认行格式。一张表的行格式也可以显式地在 CREATE TABLEALTER TABLE 语句中使用 ROW FORMAT 表选项定义。查看 Defining the Row Format of a Table 。

主键

建议在创建的每张表上都定义一个主键。在选择主键列时,选择具有如下特征的列:

  • 最重要的查询所引用的列。
  • 从不留空的列。
  • 从不具有重复值的列。
  • 插入后很少改变值的列。

例如,在一个包含人员信息的表中,你不会在 (firstname, lastname) 上创建主键,因为存在多个同名的人,名字可以为空,有时人们可以改名。由于如此多的限制,通常不存在一个明显的列的集合可以用作主键,你可以创建一个数据类型为数值型ID的新列作为主键的全部或一部分。你可以声明一个自增(auto-increment) 列,以便在插入行时自动填充升序值。

# The value of ID can act like a pointer between related items in different tables.
CREATE TABLE t5 (id INT AUTO_INCREMENT, b CHAR (20), PRIMARY KEY (id));

# The primary key can consist of more than one column. Any autoinc column must come first.
CREATE TABLE t6 (id INT AUTO_INCREMENT, a INT, b CHAR (20), PRIMARY KEY (id,a));

更多关于自增列的信息,查看 15.6.1.6 InnoDB 对自增(AUTO_INCREMENT)的处理 。

尽管不定义主键表也能正常工作,主键仍涉及到性能的许多方面,对于任何大型或经常使用的表来说,它都是一个非常重要的设计方面。建议你始终在 CREATE TABLE 语句中指定主键。如果你创建表,加载数据,然后运行 ALTER TABLE 来添加主键,这种操作比在创建表时定义主键要慢得多。有关主键的更多信息,参考 15.6.2.1 聚簇索引和次级索引 。

查看 InnoDB表的属性

执行 SHOW TABLE STATUS 语句以查看 InnoDB表的属性:

mysql> SHOW TABLE STATUS FROM test LIKE 't%' \G;
*************************** 1. row ***************************
           Name: t1
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 0
 Avg_row_length: 0
    Data_length: 16384
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: NULL
    Create_time: 2021-02-18 12:18:28
    Update_time: NULL
     Check_time: NULL
      Collation: utf8mb4_0900_ai_ci
       Checksum: NULL
 Create_options: 
        Comment:

有关 SHOW TABLE STATUS 的信息,查阅 Section 13.7.7.38, “SHOW TABLE STATUS Statement” 。

也可以查询 InnoDB 信息模式系统表来访问 InnoDB 表的属性:

mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLES WHERE NAME='test/t1' \G
*************************** 1. row ***************************
     TABLE_ID: 1144
         NAME: test/t1
         FLAG: 33
       N_COLS: 5
        SPACE: 30
   ROW_FORMAT: Dynamic
ZIP_PAGE_SIZE: 0
   SPACE_TYPE: Single
 INSTANT_COLS: 0

更多信息查阅 Section 15.15.3, “InnoDB INFORMATION_SCHEMA Schema Object Tables”

15.6.1.2 在(数据目录)外部创建表

在外部创建表有不同原因;即在数据目录外部创建表。例如,这些原因可能包括空间管理,I/O优化,或者将表放置在拥有特殊性能或容量特性的存储设备上。

InnoDB 支持以下列方式在外部创建表:

  • 使用 DATA DIRECTORY 子句
  • 使用 CREATE TABLE ... TABLESPACE 语法
  • 在外部通用表空间中创建表

使用 DATA DIRECTORY 子句

可以在 CREATE TABLE语句中指定 DATA DIRECTORY 子句来在外部目录中创建 InnoDB 表。默认情况下,当 innodb_file_per_table 变量被启用时,表隐式地创建于单表表空间。

CREATE TABLE t1 (c1 INT PRIMARY KEY) DATA DIRECTORY = '/external/directory';

创建在单表表空间中的表支持 DATA DIRECTORY 子句。

mysql> SELECT @@innodb_file_per_table;
+-------------------------+
| @@innodb_file_per_table |
+-------------------------+
|                       1 |
+-------------------------+

当你在 CREATE TABLE语句中指定 DATA DIRECTORY 子句时,会在指定目录下的一个模式目录中创建该表的数据文件(table_name.ibd)。

MySQL 8.0.21 以后,使用 DATA DIRECTORY 子句创建于数据目录外的表和表分区,仅限于 InnoDB 已知的目录。这个要求允许数据库管理员控制表空间数据文件的存储位置,确保在恢复(见 Tablespace Discovery During Crash Recovery)时可以找到数据文件。已知目录是那些由 datadir ,innodb_data_home_dirinnodb_directories 变量所定义的目录。可以使用如下语句检查这些设置:

mysql> SELECT @@datadir,@@innodb_data_home_dir,@@innodb_directories;

如果你要使用的目录(对于 MySQL InnoDB)是未知的,在创建表前将它添加到 innodb_directories 设置中。 innodb_directories 是只读的,配置它需要重启服务器。

下例展示了怎样使用 DATA DIRECTORY 子句在外部目录创建表。假设innodb_file_per_table 变量已被启用,且该目录对 InnoDB 是已知的。

mysql> USE test;
Database changed

mysql> CREATE TABLE t1 (c1 INT PRIMARY KEY) DATA DIRECTORY = '/external/directory';

# MySQL creates the table's data file in a schema directory
# under the external directory

$> cd /external/directory/test
$> ls
t1.ibd

使用说明:

  • MySQL最初使表空间数据文件保持打开状态,阻止卸载设备,但如果服务器繁忙最终会关闭文件。在 MySQL 运行期间注意不要意外卸载外部设备,或在设备未连接时启动 MySQL 。在有关数据文件丢失时访问表将导致需要服务器重启的严重错误。
    如果在预期路径上未找到数据文件,服务器重启可能会失败。这种情况下,可以从备份中恢复表空间数据文件,或删除该表以从数据字典中移除与其相关的信息。
  • 在将表放置于挂载NFS的卷前,检查 Using NFS with MySQL 中所描述的潜在问题。
  • 如果使用LVM快照,文件副本或其他基于文件的机制来备份表的数据文件,总是先使用 FLUSH TABLES ... FOR EXPORT 来确保所有缓存于内存中的更改在备份发生前刷新到磁盘。
  • 使用 DATA DIRECTORY 子句在外部目录创建表是使用符号链接的替代方案,InnoDB 不支持符号链接。
  • 源和副本位于同一主机的复制环境不支持使用 DATA DIRECTORY 子句。 DATA DIRECTORY 子句需要完整目录路径,在此案例中,复制这个路径将导致源和副本在同一位置创建表。
  • MySQL 8.0.21 以后,创建于单表表空间的表不再创建在撤销(undo)表空间目录(innodb_undo_directory),除非 InnoDB 直接知道这一点。已知目录由 datadir ,innodb_data_home_dirinnodb_directories 变量所定义。

使用 CREATE TABLE ... TABLESPACE 语法

组合使用CREATE TABLE ... TABLESPACE 语法与 DATA DIRECTORY 子句在外部目录创建表。为此,请指定 innodb_file_per_table 作为表空间名。

mysql> CREATE TABLE t2 (c1 INT PRIMARY KEY) TABLESPACE = innodb_file_per_table
       DATA DIRECTORY = '/external/directory';

该方法仅支持在单表表空间创建的表,而且不需要启用 innodb_file_per_table 变量。在所有其他方面,该方法等同于上文描述的 CREATE TABLE ... DATA DIRECTORY 方法,也适用同样的用法说明。

在外部通用表空间中创建表

可以在位于外部目录的普通表空间中创建表。

  • 关于在外部目录创建普通表空间的信息,见 创建普通表空间
  • 关于在普通表空间创建表的信息,见 向普通表空间添加表

15.6.1.3 导入 InnoDB 表

本节描述如何使用 可传输表空间(Transportable Tablespaces ) 特性导入表。它允许导入位于单表表空间的表、分区表、独立的表分区。有很多你想要导入表的理由:

  • 在非生产 MySQL 服务器实例上运行报告,以避免给生产服务器带来额外的负载。
  • 复制数据到一个新副本服务器。
  • 从备份表空间文件恢复表。
  • 作为一种比导入转储文件更快的方式,后者需要重新插入数据和构建索引。
  • 将数据移动到使用更适合您的存储要求的存储介质的服务器上。例如,您可以将繁忙的表移动到SSD设备上,或将大表移动到高容量的HDD设备上。

本节以下话题介绍了 可传输表空间 功能:

  • 先决条件
  • 导入表
  • 导入分区表
  • 导入表分区
  • 限制
  • 用法说明
  • 内部信息

先决条件

  • 启用 innodb_file_per_table ,默认启用。
  • 表空间的页大小必须和目标 MySQL 服务器实例的页大小匹配。 InnoDB 页大小由 innodb_page_size 变量定义,该变量必须在初始化 MySQL 服务器实例时配置。
  • 如果表包含外键关系, ***foreign_key_checks *** 必须在执行DISCARD TABLESPACE前被禁用。另外,你应该在同一个逻辑时间点导出外键相关表,因为ALTER TABLE ... IMPORT TABLESPACE在导入的数据上不强制外键约束。为此,停止更新相关表,停止更新相关事务,获取这些表上的共享锁,并执行导出操作。
  • 当从另一个 MySQL服务器实例导入表时,两个 MySQL 服务器实例必须拥有相同的General Availability (GA) 状态,且必须是相同版本。否则,必须在同一个要导入的 MySQL 服务器实例上创建该表。
  • 如果通过在CREATE TABLE 语句中指定DATA DIRECTORY子句在外部目录创建表,则你在目标实例上创建的表必须以同样的DATA DIRECTORY子句定义。如果模式不匹配可能会报模式不匹配错误。为了确定源表是否是使用DATA DIRECTORY子句定义的,使用SHOW CREATE TABLE查看表定义。
  • 如果 行格式(ROW_FORMAT ) 在表定义中没有显式定义,或者使用 ROW_FORMAT=DEFAULT,源和目标实例上必须具有相同的innodb_default_row_format 配置。否则,在你尝试导入操作时会报模式不匹配错误。使用SHOW CREATE TABLE来检查表定义。使用SHOW VARIABLES来检查 innodb_default_row_format 配置。

导入表

这个例子阐述了如何导入一个位于单表表空间的常用非分区表。

  1. 在目标实例上,使用和你要导入的表相同的定义创建表。(可以使用SHOW CREATE TABLE语法获取表定义。)如果表定义不匹配,在你尝试导入操作时会报模式不匹配错误。
mysql> USE test;
mysql> CREATE TABLE t1 (c1 INT) ENGINE=INNODB;
mysql> USE test;
mysql> CREATE TABLE t1 (c1 INT) ENGINE=INNODB;3
  1. 在目标实例上,丢弃你刚刚创建的表的表空间。(在导入前,你必须丢弃接收表的表空间。)
mysql> ALTER TABLE t1 DISCARD TABLESPACE;
  1. 在源实例上,运行FLUSH TABLES ... FOR EXPORT来使你要导入的表(保持)静默。当一个表是静默的,仅允许在表上执行只读事务。
mysql> USE test;
mysql> FLUSH TABLES t1 FOR EXPORT;

FLUSH TABLES ... FOR EXPORT确保对指定表的修改刷新到磁盘,以使在服务器运行期间可以执行二进制表复制。在FLUSH TABLES ... FOR EXPORT运行时,InnoDB 会在表的模式目录中产生一个 .cfg 元数据文件,它包含在导入操作期间用于模式验证的元数据。

说明
执行FLUSH TABLES ... FOR EXPORT的连接在操作执行期间必须保持打开,否则,由于连接关闭时锁被释放,导致**.cfg** 文件被移除。

  1. 从源实例将 .ibd 文件和 .cfg 元数据文件复制到目标实例上。例如:
$> scp /path/to/datadir/test/t1.{ibd,cfg} destination-server:/path/to/datadir/test

.ibd 文件和 .cfg 元数据文件必须在释放共享锁前复制,如下一步中所述。

说明
如果你从一个加密表空间导入表,InnoDB.cfg 元数据文件外产生一个 .cfp 文件,它必须和 .cfg 文件一起复制到目标实例上。.cfp 文件包含一个转换秘钥和一个加密表空间秘钥。在导入时,InnoDB 使用转换秘钥来解密表空间秘钥。

  1. 在源实例上,使用UNLOCK TABLES来释放由FLUSH TABLES ... FOR EXPORT语句获取的锁:
mysql> USE test;
mysql> UNLOCK TABLES;

UNLOCK TABLES操作还移除 .cfg 文件。

  1. 在目标实例上,导入表空间:
mysql> USE test;
mysql> ALTER TABLE t1 IMPORT TABLESPACE;

导入分区表

这个例子阐述了如何导入一个每个表分区位于单表表空间的分区表。

  1. 在目标实例上,使用与你要导入的分区表相同的定义创建分区表。(可以使用SHOW CREATE TABLE语法获取表定义。)如果表定义不匹配,在你尝试导入操作时会报模式不匹配错误。
mysql> USE test;
mysql> CREATE TABLE t1 (i int) ENGINE = InnoDB PARTITION BY KEY (i) PARTITIONS 3;

/datadir/test 目录中,三个分区各自都有一个 .ibd 表空间文件。

mysql> \! ls /path/to/datadir/test/
t1#p#p0.ibd  t1#p#p1.ibd  t1#p#p2.ibd
  1. 在目标实例上,丢弃分区表的表空间。(在导入前,你必须丢弃接收表的表空间。)
mysql> ALTER TABLE t1 DISCARD TABLESPACE;

该分区表的三个 .ibd 表空间文件从 /datadir/test 目录被丢弃。

  1. 在源实例上,运行FLUSH TABLES ... FOR EXPORT来使你要导入的分区表(保持)静默。当一个表是静默的,仅允许在表上执行只读事务。
mysql> USE test;
mysql> FLUSH TABLES t1 FOR EXPORT;

FLUSH TABLES ... FOR EXPORT确保对指定表的修改刷新到磁盘,以使在服务器运行期间可以执行二进制表复制。在FLUSH TABLES ... FOR EXPORT运行时,InnoDB 会在表的模式目录中为表的每个表空间文件产生一个 .cfg 元数据文件。

mysql> \! ls /path/to/datadir/test/
t1#p#p0.ibd  t1#p#p1.ibd  t1#p#p2.ibd
t1#p#p0.cfg  t1#p#p1.cfg  t1#p#p2.cfg

这些 .cfg 元数据文件包含在导入操作期间用于模式验证的元数据。FLUSH TABLES ... FOR EXPORT操作只能运行在表上,不能运行在单独的表分区上。

  1. 从源实例将 .ibd 文件和 .cfg 元数据文件复制到目标实例上。例如:
$> scp /path/to/datadir/test/t1*.{ibd,cfg} destination-server:/path/to/datadir/test

.ibd 文件和 .cfg 元数据文件必须在释放共享锁前复制,如下一步中所述。

说明
如果你从一个加密表空间导入表,InnoDB.cfg 元数据文件外产生一个 .cfp 文件,它必须和 .cfg 文件一起复制到目标实例上。.cfp 文件包含一个转换秘钥和一个加密表空间秘钥。在导入时,InnoDB 使用转换秘钥来解密表空间秘钥。

  1. 在源实例上,使用UNLOCK TABLES来释放由FLUSH TABLES ... FOR EXPORT语句获取的锁:
mysql> USE test;
mysql> UNLOCK TABLES;
  1. 在目标实例上,导入分区表的表空间:
mysql> USE test;
mysql> ALTER TABLE t1 IMPORT PARTITION p2, p3 TABLESPACE;

导入表分区

这个例子阐述了如何导入一个每个表分区位于单表表空间的分区表的单独表分区。

  1. 在目标实例上,使用与你要导入的分区表相同的定义创建分区表。(可以使用SHOW CREATE TABLE语法获取表定义。)如果表定义不匹配,在你尝试导入操作时会报模式不匹配错误。
mysql> USE test;
mysql> CREATE TABLE t1 (i int) ENGINE = InnoDB PARTITION BY KEY (i) PARTITIONS 4;

/datadir/test 目录中,三个分区各自都有一个 .ibd 表空间文件。

mysql> \! ls /path/to/datadir/test/
t1#p#p0.ibd  t1#p#p1.ibd  t1#p#p2.ibd t1#p#p3.ibd
  1. 在目标实例上,丢弃你打算从源实例导入的表分区。(在导入表分区前,你必须丢弃接收分区表的相应表分区。)
mysql> ALTER TABLE t1 DISCARD PARTITION p2, p3 TABLESPACE;

在目标实例上,两个丢弃表分区的 .ibd 表空间文件从 /datadir/test 目录被丢弃,留下如下文件:

mysql> \! ls /path/to/datadir/test/
t1#p#p0.ibd  t1#p#p1.ibd

说明
ALTER TABLE ... DISCARD PARTITION ... TABLESPACE运行在子分区表上时,分区表名和子分区表名都是允许的。当指定一个分区名时,该分区的子分区(们)包含在操作中。

  1. 在源实例上,运行FLUSH TABLES ... FOR EXPORT来使你要导入的分区表(保持)静默。当一个表是静默的,仅允许在表上执行只读事务。
mysql> USE test;
mysql> FLUSH TABLES t1 FOR EXPORT;

FLUSH TABLES ... FOR EXPORT确保对指定表的修改刷新到磁盘,以使在服务器运行期间可以执行二进制表复制。在FLUSH TABLES ... FOR EXPORT运行时,InnoDB 会在表的模式目录中为表的每个表空间文件产生一个 .cfg 元数据文件。

mysql> \! ls /path/to/datadir/test/
t1#p#p0.ibd  t1#p#p1.ibd  t1#p#p2.ibd t1#p#p3.ibd
t1#p#p0.cfg  t1#p#p1.cfg  t1#p#p2.cfg t1#p#p3.cfg

这些 .cfg 元数据文件包含在导入操作期间用于模式验证的元数据。FLUSH TABLES ... FOR EXPORT操作只能运行在表上,不能运行在单独的表分区上。

  1. 从源实例将 p2 分区和 p3 分区的 .ibd 文件和 .cfg 元数据文件复制到目标实例上。例如:
$> scp t1#p#p2.ibd t1#p#p2.cfg t1#p#p3.ibd t1#p#p3.cfg destination-server:/path/to/datadir/test```
 **.ibd** 文件和 **.cfg** 元数据文件必须在释放共享锁前复制,如下一步中所述。
  >**说明**
 >如果你从一个加密表空间导入表分区,**InnoDB****.cfg** 元数据文件外产生一个 **.cfp** 文件,它必须和 **.cfg** 文件一起复制到目标实例上。**.cfp** 文件包含一个转换秘钥和一个加密表空间秘钥。在导入时,**InnoDB** 使用转换秘钥来解密表空间秘钥。
 5. 在源实例上,使用`UNLOCK TABLES`来释放由`FLUSH TABLES ... FOR EXPORT`语句获取的锁:
```sql
mysql> USE test;
mysql> UNLOCK TABLES;
  1. 在目标实例上,导入表分区 p2p3
mysql> USE test;
mysql> ALTER TABLE t1 IMPORT PARTITION p2, p3 TABLESPACE;

限制

  • 只有位于单表表空间的表支持可传输表空间 功能,位于系统表空间和普通表空间的表不支持该功能。位于共享表空间的表无法被静默。
  • 使用 全文(FULLTEXT) 索引的表不支持FLUSH TABLES ... FOR EXPORT,因为全文查找辅助表无法被刷新。在导入完一张包含全文索引的表后,运行OPTIMIZE TABLE来重建全文索引。或者,在导出操作前删除全文索引,然后在目标实例导入完表后重建全文索引。
  • 由于 .cfg 元数据文件的限制,在导入分区表时,分区类型和分区定义的差异不会报模式不匹配错误。列差异会报模式不匹配错误。
  • MySQL 8.0.19 之前,索引键部分排序顺序信息不存储在表空间导入期间所使用的 .cfg 元数据文件中。因此,默认情况下,假定索引键部分排序顺序为升序。因此,如果导入操作中包含一张以 DESC 降序索引键部分排序顺序定义的表,而其他表不是如此,记录可能会以非预期的顺序被排序。替代方法是删除并重建受影响的索引。有关索引键部分排序顺序的信息,见 Section 13.1.15, “CREATE INDEX Statement” 。

MySQL 8.0.19 更新了 .cfg 元数据文件格式,以包含索引键部分排序顺序信息。上面描述的问题不影响使用 MySQL 8.0.19 及以上版本的服务器实例间的导入操作。

使用说明

  • 除了包含即时增加或删除列的表,ALTER TABLE ... IMPORT TABLESPACE不需要 .cfg 元数据文件来导入表。然而,在不使用 .cfg 元数据文件的导入操作期间,不会执行元数据检查,而会产生一个类似下面的警告:
Message: InnoDB: IO Read error: (2, No such file or directory) Error opening '.\
test\t.cfg', will attempt to import without schema verification
1 row in set (0.00 sec)

仅在预计不会发生模式不匹配,并且要导入的表不包含即时增加或删除列时,考虑不使用 .cfg 元数据文件的导入操作。该功能在元数据不可访问的崩溃恢复场景有用。

尝试导入使用ALGORITHM=INSTANT增加或删除列的表而不使用 .cfg 元数据文件,结果可能产生未定义行为。

  • Windows 上,InnoDB 内部以小写字母格式存储数据库、表空间和表的名称。避免因诸如 LinuxUnix 的大小写敏感的操作系统上的导入问题,使用小写名称创建所有的数据库、表空间和表。确保所有名称以小写格式创建的一个快捷方式是,在初始化服务器前设置 lower_case_table_names 为 1。(禁止使用不同于服务器初始化时的 lower_case_table_names 配置来启动服务器。)
[mysqld]
lower_case_table_names=1
  • 当在子分区表上运行ALTER TABLE ... DISCARD PARTITION ... TABLESPACEALTER TABLE ... IMPORT PARTITION ... TABLESPACE时,分区和子分区名称都被允许。当指定一个分区名时,该分区的子分区(们)都包含在操作中。

内部信息

以下信息描述了表导入过程中写入错误日志的内部信息和消息。
当在目标实例上ALTER TABLE ... DISCARD TABLESPACE运行时:

  • 表被以X模式锁定。
  • 表空间被从表中分离出来。

当在源实例上FLUSH TABLES ... FOR EXPORT运行时:

  • 为导出而刷新的表被以共享锁锁定。
  • 停止清除协调器线程。
  • 同步脏页到磁盘。
  • 写入表的元数据到 .cfg 二进制文件。

此操作预期的错误日志信息如下:

[Note] InnoDB: Sync to disk of '"test"."t1"' started.
[Note] InnoDB: Stopping purge
[Note] InnoDB: Writing table metadata to './test/t1.cfg'
[Note] InnoDB: Table '"test"."t1"' flushed to disk

当在源实例上运行UNLOCK TABLES时:

  • .cfg 二进制文件被删除。
  • 所导入表上的共享锁被释放,且清除协调器被重启。

此操作预期的错误日志信息如下:

[Note] InnoDB: Deleting the meta-data file './test/t1.cfg'
[Note] InnoDB: Resuming purge

当在目标实例上运行ALTER TABLE ... IMPORT TABLESPACE,导入算法允许为每个正在被导入的表空间执行如下操作:

  • 检查每个表空间页是否损坏。
  • 更新在每个页上的空间ID和日志序列号(LSNs)
  • 为头页(header page)验证标志并更新LSN。
  • 更新B树页。
  • 页状态设置为脏,以便将它写入磁盘。

此操作预期的错误日志信息如下:

[Note] InnoDB: Importing tablespace for table 'test/t1' that was exported
from host 'host_name'
[Note] InnoDB: Phase I - Update all pages
[Note] InnoDB: Sync to disk
[Note] InnoDB: Sync to disk - done!
[Note] InnoDB: Phase III - Flush changes to disk
[Note] InnoDB: Phase IV - Flush complete

说明
你可能收到一个表空间被丢弃(如果你丢弃了目标表的表空间)的警告,和一个说明由于缺少 .ibd 文件而无法计算统计信息的消息:

[Warning] InnoDB: Table "test"."t1" tablespace is set as discarded.
7f34d9a37700 InnoDB: cannot calculate statistics for table
"test"."t1" because the .ibd file is missing. For help, please refer to
http://dev.mysql.com/doc/refman/8.0/en/innodb-troubleshooting.html

15.6.1.4 移动或复制 InnoDB 表

本节描述移动或复制部分或所有 InnoDB 表到另一个服务器或实例的技术。比如,可以将整个 MySQL 实例移到更大、更快的服务器上;可以克隆整个 MySQL 实例到一个新的副本服务器上;可以将单独的表复制到另一个实例来开发和测试应用程序,或复制到一个数据仓库服务器用来生成报告。

Windows 上,InnoDB 内部总是以小写格式存储数据库和表的名称。为了以二进制格式在 UnixWindows 间移动数据库,使用小写格式创建所有数据库和表。实现这个目的的一个简便方式是,在创建数据库或表前将下行加入到 my.cnfmy.ini的**[mysqld]**部分:

[mysqld]
lower_case_table_names=1

移动或复制 InnoDB 表的技术包括:

  • 导入表
  • MySQL Enterprise Backup
  • 复制数据文件(冷备方式)
  • 从逻辑备份恢复

导入表

位于单表表空间的表可以从另一个 MySQL 服务器实例或使用 可传输表空间 功能的备份中导入。详见 章节 15.6.1.3 导入 InnoDB 表

MySQL Enterprise Backup

MySQL Enterprise Backup 产品允许你备份一个正在运行的 MySQL 数据库在生成数据库一致性快照时对操作产生最小干扰。当 MySQL Enterprise Backup 复制表时,读写可以继续。此外, MySQL Enterprise Backup 可以创建压缩备份文件和备份表的子集。结合 MySQL 二进制日志,可以执行基于时间点的恢复(point-in-time recovery)。 MySQL Enterprise Backup 是MySQL Enterprise订阅的一部分。

有关 MySQL Enterprise Backup 的更多信息,请查看 Section 30.2, “MySQL Enterprise Backup Overview” 。

复制数据文件(冷备方式)

可以简单地复制Section 15.18.1, “InnoDB Backup” 的"Cold Backups"下所列出的所有相关文件来移动一个 InnoDB 数据库。

InnoDB 数据和日志文件在所有拥有相同浮点数格式的平台都是二进制兼容的。如果浮点数格式不同,但在表中并未使用 FLOATDOUBLE 数据类型,那么步骤相同:简单地复制相关文件。

当你移动或复制单表(file-per-table) .ibd 文件时,源和目标系统上的数据库目录名称必须相同。存储于 InnoDB 共享表空间的表定义包含数据库名。存储在表空间文件中的事务ID和日志序列号也因数据库而异。

使用RENAME TABLE语句来将 .ibd 文件和相关表从一个数据库移动到另一个中:

RENAME TABLE db1.tbl_name TO db2.tbl_name;

如果你有一个 .ibd 文件的“干净”备份,你可以将它恢复到 MySQL 安装的初始状态,如下所示:

  1. 此表在你复制完 .ibd 文件后必须不能删除或截断,因为做这些操作会改变存储于表空间中的表ID。
  2. 执行这个ALTER TABLE语句删除当前 .ibd 文件:
ALTER TABLE tbl_name DISCARD TABLESPACE;
  1. 复制 .ibd 文件的备份到适当的数据库目录。
  2. 执行这个ALTER TABLE语句告诉 InnoDB 为这张表使用新的 .ibd 文件:
ALTER TABLE tbl_name IMPORT TABLESPACE;

说明
ALTER TABLE ... IMPORT TABLESPACE功能在导入的数据上不强制执行外键约束。

在这个语境中,一个 .ibd 文件的“干净”备份满足以下要求:

  • 在该 .ibd 文件中事务没有未提交的修改。
  • 在该 .ibd 文件中没有未合并的插入缓存条目。
  • 清除已经从该 .ibd 文件中移除了所有删除标记的索引记录。
  • mysqld 已经将该 .ibd 文件中所有修改的页面从缓冲池刷新到文件中。

你可以使用下列方法创建一个 .ibd 文件的“干净”备份:

  1. 停止 mysqld 服务器的所有活动并提交所有事务。
  2. 等待直到SHOW ENGINE INNODB STATUS表明数据库中没有活动事务,并且 InnoDB 的主线程状态是 Waiting for server activity。然后就可以创建 .ibd 文件的副本.

另一种方式是使用 MySQL Enterprise Backup 产品:

  1. 使用 MySQL Enterprise Backup 来备份 InnoDB 安装。
    2.在备份上再启动一个 mysqld 服务器,使用它来清理备份中的 .ibd 文件。

从逻辑备份恢复

可以使用像 mysqldump 一样的工具来执行逻辑备份,产生一组 SQL 语句,执行它们可以重新生成原始数据库对象定义和表数据,以便传输到另一个 SQL 服务器。使用这种方式,不需要关心格式差异和表中是否包含浮点类型数据。

为了提升此方法的性能,在导入数据时禁用 autocommit 。仅在导入整张表或部分表后执行一次提交。

15.6.1.5 将表从 MyISAM 转换为 InnoDB

如果存在为了更好的可靠性和扩展性而转换为 InnoDB 表的 **MyISAM表,转换前请查看以下指南和提示:

注意
在之前版本 MySQL 中创建的 MyISAM 分区表与 MySQL 8.0 不兼容。这些表必须在升级之前准备好,要么移除分区,要么将它们转换为 InnoDB。详见 Section 24.6.2, “Partitioning Limitations Relating to Storage Engines”

暂略,因本人目前不关注这块内容。

15.6.1.6 InnoDB 对自增(AUTO_INCREMENT)的处理


** InnoDB** 提供一个可配置的锁机制,它可以显著提高使用 AUTO_INCREMENT 列向表中添加行的 SQL 语句的可扩展性和性能。对 InnoDB 表使用 AUTO_INCREMENT 机制,一个 AUTO_INCREMENT 列必须被定义为一些索引的第一个或唯一的列,以便可以在表上执行一个被索引的SELECT MAX(ai_col)查找的等效操作,来获取最大列值。这个索引不需要是主键唯一键,但为了防止在 AUTO_INCREMENT 上出现重复值,建议使用这些索引类型。

本节描述 AUTO_INCREMENT 锁模式,不同 AUTO_INCREMENT 锁模式的使用含义,和 InnoDB 初始化 AUTO_INCREMENT 计数器的方式。

InnoDB 自增(AUTO_INCREMENT )锁模式

本节描述用于生成自增值的 AUTO_INCREMENT 锁模式,以及每种锁模式如何影响复制(replication)。自增锁模式在启动时使用 innodb_autoinc_lock_mode 变量配置。

以下项目用于描述 innodb_autoinc_lock_mode 配置:

  • “类 INSERT ”语句
    所有在表中产生新行的语句,包括INSERTINSERT ... SELECTREPLACE,,REPLACE ... SELECT,和 LOAD DATA。覆盖“简单插入”、“批量插入”和“混合模式插入”。
  • “简单插入”
    要插入的行数可预先确定的语句(语句初始处理时)。它包括不包含嵌套子查询的单行、多行INSERTREPLACE语句,但不包括INSERT ... ON DUPLICATE KEY UPDATE.语句。
  • “批量插入”
    要插入的行数(和需要的自增值数量)无法预先确定的语句。它包含INSERT ... SELECTREPLACE ... SELECT,和LOAD DATA 语句,但不包含简单INSERT语句。InnoDB随着每行被处理时为自增列一次分配一个新值。
  • “混合模式插入”
    包含一些为部分(并非全部)新行指定自增值的简单插入语句。如下例所示,c1t1 表上的一个自增列:
    INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');
    
    另一种类型的“混合模式插入”是INSERT ... ON DUPLICATE KEY UPDATE,实际上最糟糕的情况是INSERT然后UPDATE,在更新阶段是否使用分配的自增值都有可能。

innodb_autoinc_lock_mode 变量有三种可选值,它们是0、1、2,分别代表“传统”、“连续”、“交替”锁模式。MySQL 8.0 以后,交替锁模式(innodb_autoinc_lock_mode=2)为默认配置。MySQL 8.0 之前,连续锁模式是默认配置(innodb_autoinc_lock_mode=1)。

在 MySQL 8.0 中以交替锁模式为默认配置反映出默认复制类型由基于语句的复制到基于行的复制的变化。基于语句的复制需要连续锁模式来确保为给定 SQL 语句序列分配可预测且可重复的自增值,而基于行的复制对 SQL 语句的执行顺序并不敏感。

  • innodb_autoinc_lock_mode = 0 (“传统”锁模式)
    传统锁模式提供在引入 innodb_autoinc_lock_mode 变量前就存在的相同行为。该选项为了向后兼容,性能测试和使用“混合模式插入”因语义上可能存在的差异而导致的问题而提供。

    在此锁模式下,所有的“类 INSERT ”语句为对使用自增列的表的插入获取一个特殊的 AUTO-INC 表级锁。该锁通常持有到语句结束,以确保以一个可预期且可重复的顺序为给定的INSERT语句序列分配自增值,并确保为任何给定语句分配的自增值是连续的。

    在基于语句的复制中,这意味着当复制一个 SQL 语句到一个副本服务器时,自增列使用和源数据库相同的值。多个INSERT语句的执行结果是确定的,而且副本服务器再次产生和源服务器相同的数据。如果由多个INSERT语句生成的自增值是交替的,两个并发INSERT语句的结果是不确定的,并且无法使用基于语句的复制来可靠地传播到副本服务器。

    为了将其解释清楚,考虑一个使用这张表的例子:

    CREATE TABLE t1 (
    c1 INT(11) NOT NULL AUTO_INCREMENT,
    c2 VARCHAR(10) DEFAULT NULL,
    PRIMARY KEY (c1)
    ) ENGINE=InnoDB;
    

    假设有两个事务正在运行,每个都使用自增列向表中插入行。一个事务使用INSERT ... SELECT语句插入 1000 行,而另一个使用简单INSERT语句插入一行:

    Tx1: INSERT INTO t1 (c2) SELECT 1000 rows from another table ...
    Tx2: INSERT INTO t1 (c2) VALUES ('xxx');
    

    InnoDB 事先并不知道事务 Tx1 内的INSERT语句中的SELECT会返回多少行,而且它在语句处理过程中每次分配一个自增值。由于表级锁一直持有到语句结束,每次只能执行一个参照 t1 表的INSERT语句,不同语句生成的自增数值不是交替的。Tx1 INSERT ... SELECT生成的自增值是连续的,Tx2 INSERT语句生成的(单个)自增值或者小于或者大于所有 Tx1 生成的自增值,基于哪个语句执行在先。

    只要 SQL 语句被从二进制日志重放时以相同的顺序执行(当使用基于语句的复制时或在恢复方案),结果就和 Tx1Tx2 第一次执行时一样。因此,对于使用基于语句的复制,持有表级锁直到语句结束,让使用自增的INSERT语句变得安全。然而,当多个事务同时执行插入语句时,这些表级锁限制了并发和可扩展性。

    在之前的例子中,如果没有表级锁,Tx2 中用于INSERT的自增列值精确地取决于执行语句的时间。如果 Tx2INSERT语句执行在 Tx1 运行期间(而不是在它开始之前或完成之后),两个INSERT语句分配的指定自增值是不确定的,每次运行结果可能不同。

    在连续锁模式下,InnoDB 可以避免在插入行数已知的情况下为“简单插入”语句使用 AUTO-INC 表级锁,仍然可以保证基于语句的复制的确定性执行和安全性。
    如果不使用二进制日志来重放 SQL 语句作为恢复和复制的一部分,为了获得更高的并发性和性能,可以使用交替锁模式来消除所有对 AUTO-INC 表级锁的使用,以允许语句分配的自增值存在间隙和潜在地使并发执行的语句所分配到自增数值是交替的为代价。

  • innodb_autoinc_lock_mode = 1 (“连续”锁模式)
    在该模式下,“批量插入”使用特殊的 AUTO-INC 表级锁并持有它直到语句结束。这应用于所有的INSERT ... SELECTREPLACE ... SELECTLOAD DATA语句。一次只能执行一条持有 AUTO-INC 锁的语句。如果批量插入的源表不同于目标表,目标表上的 AUTO-INC 锁,在对从源表查找的第一行获取到共享锁后,可以被获取。如果批量插入操作的源表和目标表是同一张表,AUTO-INC 锁在对所有查到的行获取到共享锁后被获取。

    “简单插入”(要插入的行数预先知道)通过在互斥锁(一种轻量级锁)的控制下获得所需数量的自增值来避免表级 AUTO-INC 锁。互斥锁仅在分配过程期间保持,而不是直到语句完成。除非另一个事务持有AUTO-INC锁,否则不会使用表级 AUTO-INC 锁。如果另一个事务持有 AUTO-INC 锁,则“简单插入”会等待 AUTO-INC 锁,就像它是“批量插入”一样。

    这种锁定模式确保,在行数事先不知道(并且自增数值是在语句执行过程中分配的)的INSERT语句面前,,分配给任何“类 INSERT ”语句的所有自动递增值都是连续的,并且该操作对于基于语句的复制是安全的。

    简单地说,这种锁定模式显著提高了可伸缩性,同时可以安全地用于基于语句的复制。此外,与“传统”锁定模式一样,任何给定语句所分配的自增数值都是连续的。与任何使用自动增量的语句的“传统”模式相比,语义没有变化,但有一个重要的例外。
    这个例外是“混合模式插入”,用户为 AUTO_INCREMENT 列提供显式值,用于多行“简单插入”中的某些行,但不是所有行。对于这种插入,InnoDB 分配的自增值比要插入的行数更多。但是,所有自动分配的值都是连续生成的(因此大于)最近执行的前一条语句生成的自增值。“多余”数字被丢弃。

  • innodb_autoinc_lock_mode = 2 (“交替”锁模式)
    在这种锁模式下,没有“类 INSERT ”语句使用表级 AUTO-INC 锁,并且可以同时执行多个语句。这是最快、最可扩展的锁模式,但当使用基于语句的复制或恢复方案从二进制日志重放SQL语句时,这是不安全的。

InnoDB 自增锁模式使用说明

  • 对复制使用自增
    如果你正在使用基于语句的复制,设置 innodb_autoinc_lock_mode01 ,并在源和副本()服务器)上使用相同的值。如果使用 innodb_autoinc_lock_mode=2(“交错”) 或源和副本不使用相同锁模式的配置,则无法确保副本上的自增值与源上的相同。

    如果使用基于行的复制或混合格式复制,所有自增锁模式都是安全的,因为基于行的副本对SQL语句的执行顺序不敏感(混合格式对基于语句的副本不安全的任何语句都使用基于行的复制)。

  • “丢失”的自增值和序列间隙
    在所有锁模式( 0、1 和 2 )中,如果生成自动增量值的事务回滚,则这些自增值将“丢失”。一旦为自动递增列生成了一个值,它就不能回滚,无论“类 INSERT ”语句是否完成,也不管包含的事务是否回滚。这些丢失的值不会重复使用。因此,表的自增列中存储的值可能存在间隙。

  • 为自增列指定 NULL 或 0
    在所有锁模式( 0、1 和 2 )中,如果用户在INSERT中为自增列指定 NULL 或 0 ,InnoDB 会将该行视为未指定值,并为其生成新值。

  • 为自增列指定负值
    在所有锁模式( 0、1 和 2 )中,如果为auto_increment列指定负值,则自增机制的行为未定义。

  • 如果自增值大于指定整数类型的最大整数
    在所有锁模式( 0、1 和 2 )中,如果值大于可以存储在指定整数类型中的最大整数,则自增机制的行为是未定义的。

  • “批量插入”的自增值中的间隙
    innodb_autoinc_lock_mode 设置为 0(“传统”)1(“连续”) 时,任何给定语句生成的自增值都是连续的,没有间隙,因为表级 AUTO-INC 锁一直保持到语句结束,一次只能执行一个这样的语句。
    innodb_autoinc_lock_mode 设置为 2(“intervalered”) 时,“批量插入”生成的自增值可能会有间隙,但仅当存在并发执行的“类INSERT”语句时。
    对于锁模式 12 ,连续语句之间可能会出现间隙,因为对于批量插入,可能不知道每个语句所需的自增值的确切数量,并且可能会高估。

  • “混合模式插入”指定的自动增量值
    考虑一个“混合模式插入”,其中“简单插入”指定一些(但不是所有)结果行的自动增量值。这种语句在锁模式 012 下的行为不同。例如,假设 c1 是表 t1 的自增列,并且最近自动生成的序列号是 100

    mysql> CREATE TABLE t1 (
      -> c1 INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 
      -> c2 CHAR(1)
      -> ) ENGINE = INNODB;
    

    现在,考虑以下“混合模式插入”语句:

    mysql> INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');
    

    innodb_autoinc_lock_mode 设置为 0(“传统”) 时,四个新行是:

    mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
    +-----+------+
    | c1  | c2   |
    +-----+------+
    |   1 | a    |
    | 101 | b    |
    |   5 | c    |
    | 102 | d    |
    +-----+------+
    

    下一个可用的自增值是 103 ,因为自增值一次分配一个,而不是在语句执行开始时一次性分配。无论是否存在并发执行的“类 INSERT ”语句(任何类型),这个结果都是正确的。

    innodb_autoinc_lock_mode 设置为 1(“连续”) 时,四个新行也为:

    mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
    +-----+------+
    | c1  | c2   |
    +-----+------+
    |   1 | a    |
    | 101 | b    |
    |   5 | c    |
    | 102 | d    |
    +-----+------+
    

    但是,在这种情况下,下一个可用的自增值是 105 ,而不是 103 ,因为在处理语句时分配了四个自动增量值,但只使用了两个。无论是否存在并发执行的“类 INSERT ”语句(任何类型),这个结果都是正确的。
    innodb_autoinc_lock_mode 设置为 2(“交错”) 时,四个新行为:

    mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
    +-----+------+
    | c1  | c2   |
    +-----+------+
    |   1 | a    |
    |   x | b    |
    |   5 | c    |
    |   y | d    |
    +-----+------+
    

    xy 的值是唯一的,并且比以前生成的任何行都大。但是,xy 的具体值取决于并发执行语句生成的自动递增值的数量。

    最后,考虑以下语句,当最近生成的序列号为 100 时执行:

    mysql> INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (101,'c'), (NULL,'d');
    

    对于任何 innodb_autoinc_lock_mode 设置,此语句都会生成重复键错误 23000(无法写入;表中存在重复键) ,因为为行(NULL,‘b’)分配了 101 ,而插入行(101,‘c’)失败。

  • 修改INSERT语句序列中间的自增列值
    在 MySQL 5.7 及更早版本中,修改INSERT语句序列中间的自增列值可能会导致“重复条目”错误。例如,如果执行了UPDATE操作,将自增列值更改为大于当前最大自增值的值,则后续的没有指定未使用的自增值的INSERT操作,可能会遇到“重复条目”错误。在 MySQL 8.0 及更高版本中,如果将自增列值修改为大于当前最大自增值的值,则会保留新值,随后的INSERT操作会从新的较大值开始分配自动增量值。下面的示例演示了此行为。

    mysql> CREATE TABLE t1 (
      -> c1 INT NOT NULL AUTO_INCREMENT,
      -> PRIMARY KEY (c1)
      ->  ) ENGINE = InnoDB;
    
    mysql> INSERT INTO t1 VALUES(0), (0), (3);
    
    mysql> SELECT c1 FROM t1;
    +----+
    | c1 |
    +----+
    |  1 |
    |  2 |
    |  3 |
    +----+
    
    mysql> UPDATE t1 SET c1 = 4 WHERE c1 = 1;
    
    mysql> SELECT c1 FROM t1;
    +----+
    | c1 |
    +----+
    |  2 |
    |  3 |
    |  4 |
    +----+
    
    mysql> INSERT INTO t1 VALUES(0);
    
    mysql> SELECT c1 FROM t1;
    +----+
    | c1 |
    +----+
    |  2 |
    |  3 |
    |  4 |
    |  5 |
    +----+
    

InnoDB 自增计数器初始化

本节描述 InnoDB 如何初始化自增计数器。

如果为 InnoDB 表指定自增列,内存中的表对象包含一个称为自增计数器的特殊计数器,该计数器在为该列分配新值时使用。

在 MySQL 5.7 及更早版本中,自增计数器存储在主内存中,而不是存储在磁盘上。为了在服务器重新启动后初始化自增计数器,InnoDB 将在第一次插入包含自增列的表时执行等效于以下语句的语句。

SELECT MAX(ai_col) FROM table_name FOR UPDATE;

在 MySQL 8.0 中,此行为已更改。每次更改时,当前最大自增计数器值都会写入重做日志,并在每个检查点将其保存到数据字典中。这些更改使当前最大自增计数器值在服务器重新启动时保持不变。

在服务器正常关闭后重新启动时,InnoDB 使用数据字典中存储的当前最大自增值初始化内存中的自增计数器。

在崩溃恢复期间服务器重新启动时,InnoDB 使用数据字典中存储的当前最大自增值初始化内存中的自增计数器,并扫描重做日志以查找自上次检查点以来写入的自增计数值。如果重做记录的值大于内存中的计数器值,则应用重做记录值。但是,在意外服务器退出的情况下,不能保证重用以前分配的自增值。每次由于INSERTUPDATE操作而更改当前最大自增值时,都会将新值写入重做日志,但如果在将重做日志刷新到磁盘之前发生意外退出,则在服务器重新启动后初始化自增计数器时,可以重新使用以前分配的值。

InnoDB 使用等效于SELECT MAX(ai_col) FROM table_name FOR UPDATE语句来初始化自增计数器的唯一情况是导入没有 .cfg 元数据文件的表时。否则,当前最大自增计数器值将从 .cfg 元数据文件中读取(如果存在)。除了计数器值初始化之外,当试图使用ALTER TABLE ... AUTO_INCREMENT = N FOR UPDATE语句将计数器值设置为小于或等于永久的计数器值时,SELECT MAX(ai_col) FROM table_name语句的等效语句用于确定表的当前最大自动递增计数器值。例如,在删除一些记录后,您可能会尝试将计数器值设置为较小的值。在这种情况下,必须搜索表以确保新计数器值不小于或等于当前实际最大计数器值。

在 MySQL 5.7 及更早版本中,服务器重新启动会取消AUTO_INCREMENT=N表选项的效果,该选项可以分别用于CREATE TABLEALTER TABLE语句中来设置初始计数器值或更改现有计数器值。在 MySQL 8.0 中,服务器重启不会取消AUTO_INCREMENT=N表选项的效果。如果将自增计数器初始化为特定值,或者将自动递增计数值更改为更大的值,则新值将在服务器重新启动时保持。

注意
ALTER TABLE…AUTO_INCREMENT=N只能将自增计数器值更改为大于当前最大值的值。

在 MySQL 5.7 及更早版本中,在ROLLBACK操作后立即重新启动服务器可能会导致重用以前分配给回滚事务的自增值,从而有效回滚当前最大的自增值。在 MySQL 8.0 中,当前的最大自增值被持久化,从而阻止了对以前分配的值的重用。

如果使用SHOW TABLE STATUS语句在初始化自增计数器之前检查表,InnoDB 将打开表,并使用数据字典中存储的当前最大自增值初始化计数器值。然后,该值存储在内存中,供以后的插入或更新使用。计数器值的初始化使用表上的普通排他锁读取,该读取持续到该事务结束。InnoDB 在为新创建的表初始化自增计数器时遵循相同的步骤,该表的用户指定的自增值大于0。

初始化自增计数器后,如果在插入行时未明确指定自增值,InnoDB 会隐式增加计数器并将新值分配给列。如果插入的行明确指定了自增列值,并且该值大于当前最大计数器值,则计数器将设置为指定值。

只要服务器运行,InnoDB 就会使用内存中的自增计数器。当服务器停止并重新启动时,InnoDB 会重新初始化自增计数器,如前所述。

auto_increment_offset 变量确定自增列值的起点。默认设置为 1

auto_increment_increment 变量控制连续列值之间的间隔。默认设置为 1

说明

当自增整数列的值用完时,后续的INSERT操作将返回一个重复的键错误。这是MySQL的常规行为。

15.6.2 索引

本节包含 InnoDB 索引相关话题。

15.6.2.1 聚集索引和次级索引

每个 InnoDB 表都有一个称为聚集(或 簇)索引的特殊索引,用于存储行数据。通常,聚集索引与主键同义。为了从查询、插入和其他数据库操作中获得最佳性能,必须了解 InnoDB 如何使用聚集索引来优化常见的查找和 DML 操作。

  • 在表上定义PRIMARY KEY时,InnoDB 将其用作聚集索引。应为每个表定义主键。如果没有用于主键的逻辑唯一且非空列或列集,请添加一个自增列。自增列值是唯一的,在插入新行时自动添加。

  • 如果没有为表定义PRIMARY KEY,InnoDB 将使用第一个其中所有键列都定义为NOT NULLUNIQUE索引作为聚集索引。

  • 如果表没有PRIMARY KEY或合适的UNIQUE索引,InnoDB 会在包含行 ID 值的合成列上生成名为GEN_CLUST_INDEX的隐藏聚集索引。这些行按 InnoDB 分配的行 ID 排序。行 ID 是一个 6 字节字段,随着新行的插入而单调递增。因此,按行 ID 排序的行在物理上是按插入顺序排列的。

聚集索引是如何加速内存的

通过聚集索引访问行很快,因为索引查找直接指向包含行数据的页面。如果表很大,相较于使用与索引记录不同的页面存储行数据的存储组织,聚集索引结构通常可以节省磁盘 I/O 操作。

次级索引与聚集索引的关系

除聚集索引以外的索引称为次级(或 辅助,二级)索引。在 InnoDB 中,次级索引中的每条记录都包含该行的主键列,以及为次级索引指定的列。InnoDB使用此主键值搜索聚集索引中的行。

如果主键很长,则次级索引会占用更多空间,因此主键较短是有利的。

有关利用 InnoDB 聚集索引和次级索引的指南,请参阅 第8.3节“优化和索引”。

15.6.2.2 InnoDB 索引的物理结构

除空间索引外,InnoDB 索引是 B树 数据结构。空间索引使用 R树,这是用于索引多维数据的专用数据结构。索引记录存储在其 B树R树 数据结构的叶页面中。索引页的默认大小为 16KB。页面大小由初始化 MySQL 实例时的 innodb_page_size 设置确定。参见 第15.8.1节“InnoDB启动配置”。

当新记录插入到 InnoDB 聚集索引中时,InnoDB 会尝试留出 1/16 的页面空间,以便将来插入和更新索引记录。如果按顺序(升序或降序)插入索引记录,则生成的索引页大约已满 15/16 页。如果以随机顺序插入记录,页面将从 1/2 满到 15/16 满。

InnoDB 在创建或重建 B树索引时执行批量加载。这种索引创建方法称为排序索引构建innodb_fill_factor 变量定义在排序索引构建期间填充的每个 B树页面上的空间百分比,剩余空间保留用于未来索引增长。空间索引不支持排序索引构建。innodb_fill_factor 设置为 100 会为聚集索引页留出 1/16 的空间,以便将来索引增长。

如果 InnoDB 索引页的填充因子低于MERGE_THRESHOLD,如果未指定,默认为 50%,InnoDB 会尝试收缩索引树以释放该页。MERGE_THRESHOLD设置同时适用于 B树和 R树索引。有关更多信息,请参阅 第15.8.11节 “配置索引页的合并阈值”。

15.6.2.3 排序索引构建

InnoDB 在创建或重建索引时执行批量加载,而不是一次插入一条索引记录。这种索引创建方法也称为 排序索引构建。空间索引不支持排序索引构建。

索引构建有三个阶段。在第一阶段,扫描聚集索引,生成索引条目并将其添加到 排序缓冲区(sort buffer) 。当排序缓冲区已满时,条目将被排序并写入临时中间文件。此过程也称为一次“运行”。在第二阶段,当一个或多个“运行”写入临时中间文件时,将对文件中的所有条目执行合并排序。在第三个也是最后一个阶段,排序的条目被插入到 B树中。在 MySQL 8.0.31 中,这个最后阶段是多线程的。

在引入排序索引构建之前,索引条目是使用 插入 APIs 一次插入一条记录到 B树中的。此方法涉及打开一个B树游标以查找插入位置,然后使用乐观插入将条目插入B树页面。如果由于页面已满而导致插入失败,将执行悲观插入,这涉及打开B树游标,并根据需要拆分和合并 B树节点,来为条目寻找空间。这种 “自上而下” 的索引构建方法的缺点是 搜索插入位置的成本 以及 B树节点的不断拆分和合并

排序索引构建使用 “自下而上” 的方法来构建索引。使用这种方法,在 B树的所有层级上都保留对最右侧叶页面的引用。为最右侧的叶页面分配必要的B树深度,并根据其排序顺序插入条目。一旦叶页面已满,节点指针将追加到父页面,并为下一次插入分配同级叶页面。此过程一直持续到插入所有条目,这可能导致插入到根级别。当分配同级页时,将释放对以前固定的叶页的引用,新分配的叶页将成为最右侧的叶页和新的默认插入位置。

为将来的索引增长保留B树页面空间

要为将来的索引增长留出空间,可以使用innodb_fill_factor变量保留一定比例的B树页面空间。例如,将innodb_fill_factor设置为80将在排序索引构建期间保留B树页面中20%的空间。此设置适用于B树叶页面和非叶页面。它不适用于用于文本或BLOB条目的外部页面。保留的空间量可能与配置的不完全相同,因为innodb_fill_factor值被解释为提示而不是硬限制。

排序索引构建和全文索引支持

全文索引支持排序索引构建。以前,使用 SQL 将条目插入全文索引。

排序索引构建和压缩表

对于压缩表,以前的索引创建方法将条目追加到压缩页和未压缩页。当修改日志(表示压缩页面上的可用空间)已满时,将重新压缩压缩页面。如果由于空间不足导致压缩失败,页面将被拆分。对于排序索引构建,条目只会追加到未压缩的页面。当未压缩的页面变满时,它会被压缩。自适应填充用于确保在大多数情况下压缩成功,但如果压缩失败,则会分割页面并再次尝试压缩。此过程将持续到压缩成功为止。有关 B-Tree 页面压缩的更多信息,请参阅 第15.9.1.5节 “InnoDB 表的压缩方式”。

排序索引构建和重做日志记录

在排序索引构建期间,将禁用 重做日志功能。反之,用一个检查点来确保索引构建能够承受意外的退出或失败。检查点强制将所有脏页写入磁盘。在排序索引构建期间,会定期向 页面清理器(page cleaner)线程发出信号,以刷新脏页,以确保可以快速处理检查点操作。通常,当干净页面的数量低于设置的阈值时,页面清理器线程会刷新脏页面。对于排序索引构建,脏页会被迅速刷新,以减少检查点开销并使 I/O 和 CPU活动 并行化。

排序索引构建和优化器统计信息

排序索引构建可能会导致优化器统计信息与以前的索引创建方法生成的统计信息不同。统计数据的差异(预计不会影响工作负载性能)是由于用于填充索引的算法不同造成的。

15.6.2.4 InnoDB 全文(Full-Text)索引

全文索引是在基于文本的列(CHARVARCHARTEXT列)上创建的,以加快对这些列中包含的数据的查询和 DML操作。

全文索引定义为CREATE TABLE语句的一部分,或使用ALTER TABLECREATE INDEX添加到现有表中。

全文搜索使用MATCH()…AGAINST语法执行。有关用法信息,请参阅 第12.10节 “全文搜索功能” 。

InnoDB 全文索引设计

InnoDB 全文索引使用反向索引设计。反向索引存储单词列表,对于每个单词,都有一个存储包含该单词的文档的列表。为了支持邻近搜索,还将每个单词的位置信息存储为字节偏移量。

InnoDB 全文索引表

一旦创建了 InnoDB 全文索引,一系列的索引表也跟着创建。如下例所示:

mysql> CREATE TABLE opening_lines (
       id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
       opening_line TEXT(500),
       author VARCHAR(200),
       title VARCHAR(200),
       FULLTEXT idx (opening_line)
       ) ENGINE=InnoDB;

mysql> SELECT table_id, name, space from INFORMATION_SCHEMA.INNODB_TABLES
       WHERE name LIKE 'test/%';
+----------+----------------------------------------------------+-------+
| table_id | name                                               | space |
+----------+----------------------------------------------------+-------+
|      333 | test/fts_0000000000000147_00000000000001c9_index_1 |   289 |
|      334 | test/fts_0000000000000147_00000000000001c9_index_2 |   290 |
|      335 | test/fts_0000000000000147_00000000000001c9_index_3 |   291 |
|      336 | test/fts_0000000000000147_00000000000001c9_index_4 |   292 |
|      337 | test/fts_0000000000000147_00000000000001c9_index_5 |   293 |
|      338 | test/fts_0000000000000147_00000000000001c9_index_6 |   294 |
|      330 | test/fts_0000000000000147_being_deleted            |   286 |
|      331 | test/fts_0000000000000147_being_deleted_cache      |   287 |
|      332 | test/fts_0000000000000147_config                   |   288 |
|      328 | test/fts_0000000000000147_deleted                  |   284 |
|      329 | test/fts_0000000000000147_deleted_cache            |   285 |
|      327 | test/opening_lines                                 |   283 |
+----------+----------------------------------------------------+-------+

前六个索引表包含反向索引,称为辅助索引表。当传入文档被标记化时,单个单词(也称为“标记”)连同位置信息和相关的DOC_ID一起插入索引表。根据单词第一个字符的字符集排序权重,单词在六个索引表中完全排序和分区。

反向索引被划分为六个辅助索引表,以支持索引并行创建。默认情况下,两个线程将单词和相关数据标记、排序并插入索引表。可以使用 innodb_ft_sort_pll_degree 变量配置执行此工作的线程数。在大型表上创建全文索引时,请考虑增加线程数。

辅助索引表名称的前缀为 fts_ ,后缀为 index_# 。每个辅助索引表通过与被索引表的 table_id 匹配的辅助索引表名称中的十六进制值,与被索引表相关联。例如,test/opening_lines表的 table_id327,十六进制值为 0x147 。如前例所示,十六进制值 “147” 出现在与 test/openng_lines 表关联的辅助索引表的名称中。

表示全文索引的 index_id 的十六进制值也会出现在辅助索引表名称中。例如,在辅助表名 test/fts_000000000000147_00000000000001c9_index_1 中,十六进制值 1c9 的十进制值为 457 。可以通过使用该值(457)查询INFORMATION_SCHEMA.INNODB_INDEXES表来验证在 opening_lines 表上定义的索引(idx)。

mysql> SELECT index_id, name, table_id, space from INFORMATION_SCHEMA.INNODB_INDEXES
       WHERE index_id=457;
+----------+------+----------+-------+
| index_id | name | table_id | space |
+----------+------+----------+-------+
|      457 | idx  |      327 |   283 |
+----------+------+----------+-------+

如果主表是在 单表表空间 中创建的,则索引表存储在它们自己的表空间中。否则,索引表存储在被索引表所在的表空间中。

上例中展示的其他索引表称为 通用索引表,用于删除处理和存储全文索引的内部状态。与为每个全文索引创建的反向索引表不同,这组表对于在特定表上创建的所有全文索引都是通用的。

即使删除全文索引,也会保留它的通用索引表。删除全文索引时,将保留为索引创建的FTS_DOC_ID列,因为删除FTS_DOC_ID列将需要重建之前被索引的表。需要使用通用索引表管理 FTS_DOC_ID列。

  • fts_*_deletedfts_*_deleted_cache
    包含已删除但其数据尚未从全文索引中删除的文档的文档ID(DOC_ID)。fts_*_deleted_cachefts_*_deleted 表的内存版本
  • fts_*_being_deletedfts_*_being_deleted_cache
    包含已删除且其数据当前正在从全文索引中删除的文档的文档ID(DOC_ID)。fts_*_being_deleted_cache 表是 fts_*_being_deleted 表的内存版本。
  • fts_*_config
    存储有关全文索引内部状态的信息。最重要的是,它存储FTS_SYNCED_DOC_ID,它标识已解析并刷新到磁盘的文档。在崩溃恢复的情况下,FTS_SYNCED_DOC_ID值用于标识尚未刷新到磁盘的文档,以便重新分析文档并将其添加回全文索引缓存。要查看此表中的数据,请查询INFORMATION_SCHEMA.INNODB_FT_CONFIG表。

InnoDB 全文索引缓存

插入文档时,将对其进行标记化,并将单个单词和关联数据插入全文索引。这个过程,即使是对于小文档,也可能导致对辅助索引表的大量小(规模)插入,从而使对这些表的并发访问成为一个争用点。为了避免这个问题,InnoDB 使用全文索引缓存来为最近插入的行临时缓存对索引表插入。这种内存中的缓存结构保留插入,直到缓存已满,然后将它们批刷新到磁盘(到辅助索引表)。您可以查询INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE表,来查看最近插入的行的标记化数据。

缓存和批处理刷新行为避免了对辅助索引表的频繁更新。对辅助索引表的频繁更新,可能会在繁忙的插入和更新期间导致并发访问问题。批处理技术还避免了同一单词的多次插入,并将重复条目减至最少。不是逐个刷新每个单词,而是将对同一单词的插入合并,然后以单个条目的形式刷新到磁盘,从而提高插入效率,同时使辅助索引表尽可能的小。

innodb_ft_cache_size 变量用于配置全文索引缓存大小(基于每个表),它会影响刷新全文索引缓存的频率。还可以使用 innodb_ft_total_cache_size 变量为给定实例中的所有表定义全局全文索引缓存大小限制。

全文索引缓存存储的信息与辅助索引表相同。但是,全文索引缓存仅缓存最近插入的行的标记化数据。查询时,已刷新到磁盘(到辅助索引表)的数据不会带回全文索引缓存。直接查询辅助索引表中的数据,并在返回之前将辅助索引表的结果与全文索引缓存的结果合并。

全文索引的 DOC_ID 和 FTS_DOC_ID

InnoDB 使用名为DOC_ID的唯一文档标识符将全文索引中的单词映射到出现单词的文档记录。映射需要索引表上的FTS_DOC_ID列。如果未定义FTS_DOC_ID列,则在创建全文索引时,InnoDB 会自动添加隐藏的FTS_DOC _ID列。以下示例演示了此行为。

下表定义不包括FTS_DOC_ID列:

mysql> CREATE TABLE opening_lines (
       id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
       opening_line TEXT(500),
       author VARCHAR(200),
       title VARCHAR(200)
       ) ENGINE=InnoDB;

使用 CREATE FULLTEXT INDEX语法在表上创建全文索引时,将返回一条警告,报告InnoDB 正在重建表以添加FTS_DOC_ID列。

mysql> CREATE FULLTEXT INDEX idx ON opening_lines(opening_line);
Query OK, 0 rows affected, 1 warning (0.19 sec)
Records: 0  Duplicates: 0  Warnings: 1

mysql> SHOW WARNINGS;
+---------+------+--------------------------------------------------+
| Level   | Code | Message                                          |
+---------+------+--------------------------------------------------+
| Warning |  124 | InnoDB rebuilding table to add column FTS_DOC_ID |
+---------+------+--------------------------------------------------+

当使用ALTER TABLE向没有FTS_DOC_ID列的表添加全文索引时,会返回相同的警告。如果您在CREATE TABLE时创建全文索引,并且没有指定FTS_DOC_ID列,InnoDB会添加一个隐藏的FTS_DOC_ID列,而不会发出警告。

CREATE TABLE时定义FTS_DOC_ID列比在已加载数据的表上创建全文索引的成本更低。如果在加载数据之前在表上定义了FTS_DOC_ID列,则无需重新构建表及其索引即可添加新列。如果您不关心CREATE FULLTEXT INDEX性能,请省略FTS_DOC_ID列,让InnoDB 为您创建它。InnoDB 在FTS_DOC_ID列上创建一个隐藏的FTS_DOC_ID列以及一个唯一索引(FTS_DOC_ID_INDEX)。如果要创建自己的FTS_DOC_ID列,必须将该列定义为BIGINT UNSIGNED NOT NULL,并将其命名为FTS_DOC _ID(全部大写),如以下示例所示:

mysql> CREATE TABLE opening_lines (
       FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
       opening_line TEXT(500),
       author VARCHAR(200),
       title VARCHAR(200)
       ) ENGINE=InnoDB;

注意
FTS_DOC_ID列不需要定义为 AUTO_INCREMENT 列,但这么做会使加载数据更容易。

如果您选择自己定义FTS_DOC_ID列,则需要负责管理该列以避免空值或重复值。FTS_DOC_ID值不能重用,这意味着FTS_DOC _ID值必须不断增加。

或者,您可以在FTS_DOC_ID列上创建所需的唯一FTS_DOC_ID_INDEX (全部大写)。

mysql> CREATE UNIQUE INDEX FTS_DOC_ID_INDEX on opening_lines(FTS_DOC_ID);

如果你不创建FTS_DOC_ID_INDEX,InnoDB 将自动创建它。

注意
FTS_DOC_ID_INDEX不能被定义为降序索引,因为 InnoDB SQL 解析器不使用降序索引。

已使用的最大FTS_DOC_ID值和新FTS_DOC _ID值之间允许的间隙为 65535。

为了避免重建表,删除全文索引时会保留FTS_DOC_ID列。

InnoDB 全文索引删除处理

删除具有全文索引列的记录可能会导致辅助索引表中的大量小(规模)删除,从而使对这些表的并发访问成为争用点。为了避免此问题,每当从索引表中删除记录时,被删除文档的DOC_ID都会记录在一个特殊的 FTS_*_DELETED 表中,而索引记录仍保留在全文索引中。在返回查询结果之前,FTS_*_DELETED 表中的信息用于筛除已删除的DOC_ID。这种设计的好处是删除速度快、成本低。缺点是删除记录后,索引的大小不会立即减小。若要删除已删除记录的全文索引条目,请在索引表上运行OPTIMIZE TABLEinnodb_optimize_fulltext_only=ON ,以重新生成全文索引。有关详细信息,请参阅 优化 InnoDB全文索引。

InnoDB 全文索引事务处理

由于其缓存和批处理行为,InnoDB 全文索引具有特殊的事务处理特性。具体来说,在事务提交时处理全文索引上的更新和插入,这意味着全文搜索只能看到提交的数据。以下示例演示了此行为。全文搜索仅在提交插入的行后返回结果。

mysql> CREATE TABLE opening_lines (
       id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
       opening_line TEXT(500),
       author VARCHAR(200),
       title VARCHAR(200),
       FULLTEXT idx (opening_line)
       ) ENGINE=InnoDB;

mysql> BEGIN;

mysql> INSERT INTO opening_lines(opening_line,author,title) VALUES
       ('Call me Ishmael.','Herman Melville','Moby-Dick'),
       ('A screaming comes across the sky.','Thomas Pynchon','Gravity\'s Rainbow'),
       ('I am an invisible man.','Ralph Ellison','Invisible Man'),
       ('Where now? Who now? When now?','Samuel Beckett','The Unnamable'),
       ('It was love at first sight.','Joseph Heller','Catch-22'),
       ('All this happened, more or less.','Kurt Vonnegut','Slaughterhouse-Five'),
       ('Mrs. Dalloway said she would buy the flowers herself.','Virginia Woolf','Mrs. Dalloway'),
       ('It was a pleasure to burn.','Ray Bradbury','Fahrenheit 451');

mysql> SELECT COUNT(*) FROM opening_lines WHERE MATCH(opening_line) AGAINST('Ishmael');
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+

mysql> COMMIT;

mysql> SELECT COUNT(*) FROM opening_lines WHERE MATCH(opening_line) AGAINST('Ishmael');
+----------+
| COUNT(*) |
+----------+
|        1 |
+----------+

监控 InnoDB 全文索引

您可以通过查询以下INFORMATION_SCHEMA表来监视和检查 InnoDB 全文索引的特殊的文本处理方面:

  • INNODB_FT_CONFIG
  • INNODB_FT_INDEX_TABLE
  • INNODB_FT_INDEX_CACHE
  • INNODB_FT_DEFAULT_STOPWORD
  • INNODB_FT_DELETED
  • INNODB_FT_BEING_DELETED

您还可以通过查询INNODB_INDEXESINNODB_TABLES来查看全文索引和表的基本信息。

有关更多信息,请参阅 第15.15.4节 “InnoDB INFORMATION_SCHEMA FULLTEXT 索引表” 。

15.6.3 表空间

15.6.3.1 系统表空间

系统表空间是 **更改缓冲区(Change Buffer)**的存储区域。如果表是在系统表空间中,而不是每表表空间或普通表空间中创建的,则系统表空间还可能包含表和索引数据。在以前的 MySQL 版本中,系统表空间包含 InnoDB 数据字典。在 MySQL 8.0 中,InnoDB 将元数据存储在 MySQL 数据字典中。参见 第14章,MySQL 数据字典。在以前的 MySQL 版本中,系统表空间还包含双写缓冲区的存储区域。自 MySQL 8.0.20 起,此存储区域位于单独的双写文件中。请参阅 第15.6.4节 “双写缓冲区”。

系统表空间可以有一个或多个数据文件。默认情况下,在数据目录中创建一个名为 ibdata1的系统表空间数据文件。系统表空间数据文件的大小和数量由 innodb_data_file_path 启动选项定义。有关配置信息,请参阅 系统表空间数据文件配置。

有关系统表空间的其他信息,请参见本节中的以下主题:

  • 调整系统表空间的大小
  • 为系统表空间使用原始磁盘分区(Raw Disk Partition)

调整系统表空间的大小

本节介绍如何增加或减少系统表空间的大小。

增加系统表空间的大小

增加系统表空间大小的最简单方法是将其配置为自动扩展。为此,请在 innodb_data_file_path 设置中为最后一个数据文件指定autoextend属性,然后重新启动服务器。例如:

innodb_data_file_path=ibdata1:10M:autoextend

当指定了autoextend属性时,数据文件的大小会根据需要的空间自动增加 8MB。 innodb_autoextend_increment 变量控制增量大小。

您还可以通过添加另一个数据文件来增加系统表空间的大小。为此:

  1. 停止MySQL服务器。
  2. 如果 innodb_data_file_path 设置中的最后一个数据文件是用autoextend属性定义的,请删除它,然后修改size属性以反映当前数据文件的大小。要确定要指定的适当数据文件大小,请检查文件系统中的文件大小,并将该值向下舍入到最接近的 MB 值,其中 MB 等于 1024 x 1024 字节。
  3. 将新的数据文件追加到 innodb_data_file_path 设置,可以选择指定autoextend属性。只能为 innodb_data_file_path 设置中的最后一个数据文件指定autoextend属性。
  4. 启动MySQL服务器。

例如,此表空间有一个自动扩展的数据文件:

innodb_data_home_dir =
innodb_data_file_path = /ibdata/ibdata1:10M:autoextend

假设数据文件随着时间的推移增长到 988MB。这是修改size属性以反映当前数据文件大小后,以及指定新的 50MB自动扩展数据文件后的 innodb_data_file_path 设置:

innodb_data_home_dir =
innodb_data_file_path = /ibdata/ibdata1:988M;/disk2/ibdata2:50M:autoextend

添加新数据文件时,不要指定现有文件名。InnoDB 在启动服务器时创建并初始化新的数据文件。

注意
不能通过更改现有系统表空间数据文件的大小属性来增加其大小。例如,将 innodb_data_file_path 设置从ibdata1:10M:autoextend更改为ibdata1:12M:autoextend会在启动服务器时产生以下错误:

[ERROR] [MY-012263] [InnoDB] The Auto-extending innodb_system
data file './ibdata1' is of a different size 640 pages (rounded down to MB) than
specified in the .cnf file: initial 768 pages, max 0 (relevant if non-zero) pages!

该错误表示现有数据文件大小(以 InnoDB 页表示)与配置文件中指定的数据文件大小不同。如果遇到此错误,请恢复先前的 innodb_data_file_path 设置,并参阅系统表空间大小调整说明。

减小 InnoDB 系统表空间大小

不支持减小现有系统表空间的大小。实现更小系统表空间的唯一选项是将数据从备份恢复到使用所需系统表空间大小配置创建的新 MySQL 实例。

有关创建备份的信息,请参阅 第 15.18.1 节 “InnoDB备份”。

有关为新系统表空间配置数据文件的信息,请参阅 系统表空间数据文件配置 。

为了避免使用大的系统表空间,请考虑为数据使用单表表空间或普通表空间。单表表空间是默认的表空间类型,在创建 InnoDB 表时会隐式使用。与系统表空间不同,单表表空间在被截断或删除时会向操作系统返回磁盘空间。有关更多信息,请参阅 第 15.6.3.2 节 “单表表空间” 。普通表空间是多表表空间,也可以用作系统表空间的替代。参见 第 15.6.3.3 节 “普通表空间” 。

为系统表空间使用原始磁盘分区(Raw Disk Partition)

原始(或 裸)磁盘分区 可以用作系统表空间数据文件。这种技术可以在 Windows 和一些 Linux 和 Unix 系统上实现 非缓冲 I/O ,而无需文件系统开销。分别对使用和不使用原始分区执行测试,以验证它们是否提高了系统的性能。

使用原始磁盘分区时,请确保运行 MySQL 服务器的用户 ID 具有该分区的读写权限。例如,如果以 mysql 用户身份运行服务器,则分区必须是 mysql 可读写的。如果使用--memlock选项运行服务器,服务器必须以 root 身份运行,因此分区必须是 root 可读写的。
下面描述的过程涉及选项文件修改。有关更多信息,请参阅 第 4.2.2.2节 “使用选项文件”。

在 Linux 和 Unix 系统上分配原始磁盘分区
  1. 创建新数据文件时,请在 innodb_data_file_path 选项的数据文件大小之后立即指定关键字newraw。分区必须至少与指定的大小一样大。请注意,InnoDB 中的 1MB1024×1024 字节,而 磁盘规格 中的 1TB通常表示 1000000字节
[mysqld]
innodb_data_home_dir=
innodb_data_file_path=/dev/hdd1:3Gnewraw;/dev/hdd2:2Gnewraw
  1. 重新启动服务器。InnoDB 注意到newraw关键字并初始化新分区。但是,请不要创建或更改任何 InnoDB 表。否则,下次重新启动服务器时,InnoDB 会重新初始化分区,您的更改将丢失。(作为一项安全措施,InnoDB 可以防止用户在任何带有newraw的分区被指定时修改数据。)
  2. InnoDB 初始化新分区后,停止服务器,将数据文件规范中的newraw更改为raw
[mysqld]
innodb_data_home_dir=
innodb_data_file_path=/dev/hdd1:3Graw;/dev/hdd2:2Graw
  1. 重新启动服务器。InnoDB现在允许进行更改。
在 Windows 上分配原始磁盘分区

在 Windows 系统上,除了 innodb_data_file_path 设置在 Windows 上略有不同之外,适用于 Linux 和 Unix 系统的相同步骤和附带指南。

  1. 创建新数据文件时,在 innodb_data_file_path 选项的数据文件大小之后立即指定关键字newraw
[mysqld]
innodb_data_home_dir=
innodb_data_file_path=//./D::10Gnewraw

//./对应于 Windows 语法\\.\,用于访问物理驱动器。在上面的示例中,D: 是分区的驱动器号。
2. 重新启动服务器。InnoDB 注意到newraw关键字并初始化新分区。
3. InnoDB初始化新分区后,停止服务器,将数据文件规范中的newraw更改为raw

[mysqld]
innodb_data_home_dir=
innodb_data_file_path=//./D::10Graw
  1. 重新启动服务器。InnoDB 现在允许进行更改。

15.6.3.2 单表表空间


单表(File-Per-Table)表空间包含 单个 InnoDB 表的数据索引,并存储在文件系统中的单个数据文件中。

单表表空间配置

InnoDB 默认情况下在单表表空间中创建表。此行为由 innodb_file_per_table 变量控制。禁用 innodb_file_per_table 会导致 Innodb 在系统表空间中创建表。

innodb_file_per_table 设置可以在选项文件中指定,也可以在运行时使用SET GLOBAL语句进行配置。在运行时更改设置需要足够的权限来设置全局系统变量。参见第5.1.9.1节 “系统变量权限”。

选项文件:

[mysqld]
innodb_file_per_table=ON

在运行时使用SET GLOBAL语句:

mysql> SET GLOBAL innodb_file_per_table=ON;

单表表空间数据文件

单表表空间是在 MySQL 数据目录 下的模式目录中的 .ibd 数据文件中创建的。.ibd 文件是为表命名的( table_name.ibd )。例如,表 test.t1 的数据文件被创建在 MySQL 数据目录 下的 test 目录中:

mysql> USE test;

mysql> CREATE TABLE t1 (
   id INT PRIMARY KEY AUTO_INCREMENT,
   name VARCHAR(100)
 ) ENGINE = InnoDB;
$> cd /path/to/mysql/data/test
$> ls
t1.ibd

你可以使用CREATE TABLE语句的DATA DIRECTORY子句在数据目录之外隐式地创建单表表空间数据文件。

单表表空间的优势

与共享表空间(如系统表空间或常规表空间)相比,单表表空间具有以下优势:

  • 在截断或删除创建于单表表空间中的表后磁盘空间会返还给操作系统,而共享表空间则会在其数据文件中创建仅可用于 InnoDB 数据的空闲表空间。换句话说,共享表空间在截断或删除表后不收缩大小。
  • 在位于共享表空间的表上的表复制型(table-copying)ALTER TABLE操作会增加表空间占用的磁盘空间总额。这种操作可能需要与表和索引中存储的数据一样大小的额外空间。这部分空间不会像单表表空间一样释放回操作系统。
  • TRUNCATE TABLE在位于单表表空间中的表上执行时性能更好。
  • 出于 I/O 优化、空间管理、备份目的,可以将单表表空间创建于独立的存储设备上。
  • 你可以从另一个 MySQL 实例导入位于单表表空间的表。
  • 创建于单表表空间的表支持DYNAMICCOMPRESSED行格式功能,而系统表空间却不支持。
  • 在数据发生损坏,或备份和二进制日志不可用时,存储于单表表空间数据文件中的表可以节省时间,并提高成功恢复的几率。
  • 使用 MySQL Enterprise Backup 可以很快的备份和恢复创建于单表表空间的表,而不用中断其他 InnoDB 表的使用。这对于采用变化的备份计划或备份频率较低的表很有用。详见 创建部分备份
  • 单表表空间允许通过监控表空间数据文件大小来监控文件系统上的表大小。
  • innodb_flush_method = O_DIRECT 时,常见 Linux 文件系统对诸如共享表空间数据文件的单一文件的并发写。结果,将单表表空间与这个设置结合使用,可能获得性能提升。
  • 共享表空间中的所有表受限于 64TB 表空间大小限制(即共享表空间中的所有表表大小总额不超过 64TB)。相比之下,每个单表表空间有 64TB 大小限制,为单个表的大小增长提供了足够的空间。

单表表空间的缺点

与共享表空间(如系统表空间或常规表空间)相比,单表表空间具有以下缺点:

  • 使用单表表空间时,每张表可能包含仅可被同一张表内的行使用的未使用空间,如果没有适当管理,这可能导致浪费空间。
  • fsync 操作在多个单表表空间数据文件上执行,而不是在单个共享表空间数据文件上。由于fsync 操作是每(单)文件的,因而不能合并多个表的写操作,这可能导致fsync 操作总数过高。
  • mysqld 必须为每个单表表空间保持一个打开的文件句柄。如果你有大量使用单表表空间的表,这可能造成性能影响。
  • 因每张表有自己的数据文件,故需要更多的文件描述符。
  • 存在更多碎片的可能性,这会妨碍DROP TABLE和表扫描的性能。然而,如果碎片得到管理的,单表表空间可以提升这些操作的性能。
  • 在删除位于单表表空间的表时会扫描缓冲池,对于大型缓冲池,这可能需要几秒的时间。这个扫描是采用宽范围内部锁执行的,可能延迟其他操作。
  • innodb_autoextend_increment 变量,定义当自动扩展的共享表空间变满时所扩展的大小。它无法应用于单表表空间中,因为单表表空间自动扩展,而不管 innodb_autoextend_increment 如何设置。单表表空间初始少量扩展,之后以 4MB 为增量扩展。

15.6.3.3 普通表空间


普通(或常规、通用)表空间( General Tablespace),是使用CREATE TABLESPACE 语法创建的共享表空间。以下部分介绍了其功能特性。

普通表空间的功能

普通表空间提供如下功能:

  • 与系统表空间类似,普通表空间是能够存储多个表的数据的共享表空间。
  • 普通表空间比单表表空间具有潜在的内存优势。服务器在表空间的生命周期内将表空间元数据保存在内存中。与单独单表表空间文件中的表相比,相同数量的多个表存储在更少的普通表空间中,表空间元数据消耗的内存更少。
  • 常规表空间数据文件可以放置在与 MySQL 数据目录 相关或独立的目录中,这为您提供了单表空间文件的许多数据文件和存储管理功能。与单表表空间一样,将数据文件放置在 MySQL数据目录之外的能力允许您单独管理关键表的性能,例如为特定表设置 RAID 或 DRBD ,或将表绑定到特定磁盘。
  • 常规表空间支持所有的表行格式和相关功能。
  • TABLESPACE选项可以与CREATE TABLE一起使用,以在常规表空间、单表表空间或系统表空间中创建表。
  • TABLESPACE选项可以与ALTER TABLE一起使用,以在常规表空间、单表表空间和系统表空间之间移动表。

创建普通表空间

使用CREATE TABLESPACE语法创建普通表空间。

CREATE TABLESPACE tablespace_name
    [ADD DATAFILE 'file_name']
    [FILE_BLOCK_SIZE = value]
        [ENGINE [=] engine_name]

可以在数据目录中或其外部创建常规表空间。为了避免与隐式创建的单表表空间冲突,不支持在数据目录下的子目录中创建常规表空间。在数据目录之外创建常规表空间时,该目录必须存在,并且在创建表空间之前必须为 InnoDB 所知。要使 InnoDB 知道未知目录,请将该目录添加到 innodb_directories 参数值中。innodb_directories 是一个只读启动选项。配置它需要重新启动服务器。

例如:
在数据目录中创建普通表空间:

mysql> CREATE TABLESPACE `ts1` ADD DATAFILE 'ts1.ibd' Engine=InnoDB;

或:

mysql> CREATE TABLESPACE `ts1` Engine=InnoDB;

ADD DATAFILE子句在 MySQL 8.0.14 之前需要,之后则是可选的。如果在创建表空间时不指定它,将隐式地使用一个唯一的文件名创建表空间数据文件。但唯一文件名是由破折号(-)分割的五组十六进制数字组成的128位 UUID ( aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee )命名的。普通表空间数据文件包含 .ibd 文件扩展。在复制场景中,在源上创建的数据文件名不同于在副本上创建的数据文件名。

在数据目录外部的目录中创建普通表空间:

mysql> CREATE TABLESPACE `ts1` ADD DATAFILE '/my/tablespace/directory/ts1.ibd' Engine=InnoDB;

只要表空间目录不在数据目录下,就可以使用相对路径。在下例中,***my_tablespace *** 与数据目录同级:

mysql> CREATE TABLESPACE `ts1` ADD DATAFILE '../my_tablespace/ts1.ibd' Engine=InnoDB;

注意
在默认存储引擎不是 InnoDB 时,CREATE TABLESPACE必须使用ENGINE = InnoDB子句。
也可以将设置 InnoDB 为默认存储引擎( default_storage_engine = InnoDB )而不必使用ENGINE = InnoDB子句。

向普通表空间添加表


创建常规表空间后,可以使用CREATE TABLE tbl_name ... TABLESPACE [=] tablespace_nameALTER TABLE tbl_name TABLESPACE [=] tablespace_name语句将表添加到表空间,如以下示例所示:
CREATE TABLE :

mysql> CREATE TABLE t1 (c1 INT PRIMARY KEY) TABLESPACE ts1;

ALTER TABLE :

mysql> ALTER TABLE t2 TABLESPACE ts1;

注意*
向共享表空间添加表分区的支持在 MySQL 5.7.24 弃用,并且在 MySQL 8.0.13 移除。

普通表空间支持的行格式

普通表空间支持所有的表行格式(REDUNDANTCOMPACT,,DYNAMICCOMPRESSED)。注意,压缩表和非压缩表由于不同的物理页大小而不能共存于同一个普通表空间。

对包含压缩表(ROW_FORMAT=COMPRESSED)的普通表空间,必须指定FILE_BLOCK_SIZE选项,且其值必须是一个相对于 innodb_page_size 值有效的压缩页大小。同样,压缩表的物理页大小(KEY_BLOCK_SIZE)必须等于 FILE_BLOCK_SIZE/1024 。例如,如果 innodb_page_size=16KBFILE_BLOCK_SIZE=8K,表的KEY_BLOCK_SIZE必须是 8

下表展示允许的 innodb_page_sizeFILE_BLOCK_SIZE,和 KEY_BLOCK_SIZE 组合。FILE_BLOCK_SIZE值也可以用字节指定。要确定给定FILE_BLOCK_SIZE的有效 KEY_BLOCK_SIZE 值,用FILE_BLOCK_SIZE的值除以1024。表压缩不支持 32K 和 64K InnoDB 页大小。

表 15.3 压缩表允许的页大小,FILE_BLOCK_SIZE,和 KEY_BLOCK_SIZE 组合

InnoDB 页大小(innodb_page_size) 允许的 FILE_BLOCK_SIZE 值 允许的 KEY_BLOCK_SIZE 值
64KB 64K (65536) 不支持压缩
32KB 32K (32768) 不支持压缩
16KB 16K (16384) 无。如果 innodb_page_size 等于 FILE_BLOCK_SIZE,表空间不能含有压缩表。
16KB 8K (8192) 8
16KB 4K (4096) 4
16KB 2K (2048) 2
16KB 1K (1024) 1
8KB 8K (8192) 无。如果 innodb_page_size 等于 FILE_BLOCK_SIZE,表空间不能含有压缩表。
8KB 4K (4096) 4
8KB 2K (2048) 2
8KB 1K (1024) 1
4KB 4K (4096) 无。如果 innodb_page_size 等于 FILE_BLOCK_SIZE,表空间不能含有压缩表。
4KB 2K (2048) 2
4KB 1K (1024) 1

下例展示了创建普通表空间和添加压缩表。该例子假设 innodb_page_size 默认值为 16KB,FILE_BLOCK_SIZE 为 8192,要求压缩表的KEY_BLOCK_SIZE 为 8 。

mysql> CREATE TABLESPACE `ts2` ADD DATAFILE 'ts2.ibd' FILE_BLOCK_SIZE = 8192 Engine=InnoDB;

mysql> CREATE TABLE t4 (c1 INT PRIMARY KEY) TABLESPACE ts2 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;

如果你在创建普通表空间时不指定FILE_BLOCK_SIZE FILE_BLOCK_SIZE 值默认为 innodb_page_size 。当FILE_BLOCK_SIZE 等于 innodb_page_size ,该表空间仅包含非压缩行格式(COMPACTREDUNDANTDYNAMIC)的表。

使用ALTER TABLE在表空间之间移动表

使用TABLESPACE 选项的ALTER TABLE语句可以将表移动到一个已存在的通用表空间,一个新单表表空间,或系统表空间中。

为了将表从单表表空间或系统表空间移动到通用表空间,需要指定通用表空间名称,且通用表空间必须存在。详见 ALTER TABLESPACE

ALTER TABLE tbl_name TABLESPACE [=] tablespace_name;

为了将表从单表表空间或通用表空间移动到系统表空间,指定innodb_system 为表空间名。

ALTER TABLE tbl_name TABLESPACE [=] innodb_system;

为了将表从系统表空间或通用表空间移动到单表表空间,指定innodb_file_per_table 为表空间名。

ALTER TABLE tbl_name TABLESPACE [=] innodb_file_per_table;

ALTER TABLE ... TABLESPACE操作不支持将表从临时表空间移动到永久表空间。

DATA DIRECTORY子句允许和CREATE TABLE ... TABLESPACE=innodb_file_per_table一起使用,但不支持和TABLESPACE选项组合使用。 MySQL 8.0.21 以后,DATA DIRECTORY子句指定的目录必须对 InnoDB 来说是已知的。详见 使用 DATA DIRECTORY 子句

有关从加密表空间移动表的限制,请看 加密限制。

重命名普通表空间

支持使用ALTER TABLESPACE ... RENAME TO语法重命名普通表空间。

ALTER TABLESPACE s1 RENAME TO s2;

重命名通用表空间需要CREATE TABLESPACE权限。

RENAME TO操作无视 autocommit 设置而隐式地以 autocommit 模式执行。

LOCK TABLESFLUSH TABLES WITH READ LOCK对表空间中的表生效期间无法执行RENAME TO操作。

在表空间重命名时,会在通用表空间中的表上获取 排他元数据锁。元数据锁会阻止并发DDL,但支持并发DML 。

删除普通表空间

DROP TABLESPACE语句用来删除 InnoDB 普通表空间。

DROP TABLESPACE操作前必须删除该表空间中的所有表。如果表空间非空,则DROP TABLESPACE会返回一个错误。

使用类似如下查询来验证表空间中的表:

mysql> SELECT a.NAME AS space_name, b.NAME AS table_name FROM INFORMATION_SCHEMA.INNODB_TABLESPACES a,
       INFORMATION_SCHEMA.INNODB_TABLES b WHERE a.SPACE=b.SPACE AND a.NAME LIKE 'ts1';
+------------+------------+
| space_name | table_name |
+------------+------------+
| ts1        | test/t1    |
| ts1        | test/t2    |
| ts1        | test/t3    |
+------------+------------+

当删除表空间中最后一张表时,不会自动删除该表空间。必须使用DROP TABLESPACE tablespace_name语句显式地删除表空间。

普通表空间不属于任何特定的数据库。DROP DATABASE操作可以删除属于一个普通表空间的表,但却无法删除表空间,即使DROP DATABASE操作可以删除属于该表空间的所有表。

类似于系统表空间,截断或清空存储在普通表空间的表,会在该表空间的 .ibd 数据文件中,内部地创建仅可被 InnoDB 数据使用的空闲空间。空间不会释放回操作系统,而单表表空间在DROP TABLE操作期间会被(从操作系统)删除(因而会将空间释放回操作系统)。

下例阐述了如何删除一个 InnoDB 表空间。通用表空间 ts1 中创建了一张表。在删除表空间前必须先删除该表。

mysql> CREATE TABLESPACE `ts1` ADD DATAFILE 'ts1.ibd' Engine=InnoDB;

mysql> CREATE TABLE t1 (c1 INT PRIMARY KEY) TABLESPACE ts1 Engine=InnoDB;

mysql> DROP TABLE t1;

mysql> DROP TABLESPACE ts1;

注意
tablespace_name 在 MySQL 中是大小写敏感的标识符。

普通表空间限制

  • 已生成的或已存在的表空间不能改变成普通表空间。
  • 不支持创建临时普通表空间。
  • 普通表空间不支持临时表。
  • 类似于系统表空间,截断或清空存储在普通表空间的表,会在该表空间的 .ibd 数据文件中,内部地创建仅可被 InnoDB 数据使用的空闲空间。空间不会释放回操作系统,而单表表空间在DROP TABLE操作期间会被(从操作系统)删除(因而会将空间释放回操作系统)。
    在位于共享表空间的表上的表复制型(table-copying)ALTER TABLE操作会增加表空间占用的磁盘空间总额。这种操作可能需要与表和索引中存储的数据一样大小的额外空间。这部分空间不会像单表表空间一样释放回操作系统。
  • 共享表空间的表不支持ALTER TABLE ... DISCARD TABLESPACEALTER TABLE ...IMPORT TABLESPACE
  • 对在普通表空间添加表分区的支持在 MySQL 5.7.24 被弃用,在 MySQL 8.0.13 被移除。
  • 在源和副本位于同一主机的复制场景下,不支持ADD DATAFILE,因为这会导致源和副本在同一位置创建同名的表空间,而这是不支持的行为。然而,如果省略ADD DATAFILE,则允许使用一个生成的唯一文件名在数据目录中创建表空间。
  • MySQL 8.0.21 后,不能在UNDO表空间目录(innodb_undo_directory)下创建普通表空间,除非 InnoDB 直接知道该目录。已知目录是由 datadirinnodb_data_home_dirinnodb_directories 变量定义的。

15.6.3.4 撤销(UNDO)表空间

**撤销(UNDO)表空间包含撤销日志(Undo Log)。撤销日志是存储撤销事务对聚集索引记录的最近更改的信息的集合。

译者注:
本文为更清晰地表述后续采用英文原文“undo”来表达撤销相关对象。

默认 Undo 表空间

在 MySQL 实例初始化时会创建两个默认 Undo 表空间。他们在初始化时为在 SQL 语句可被接受前必须存在的回滚段提供存储位置。至少需要两个 Undo 表空间来支持自动截断(清空) Undo 表空间。参阅 清空 Undo 表空间。

默认 Undo 表空间创建于 innodb_undo_directory 变量定义的位置。如果未定义 innodb_undo_directory ,默认创建于数据目录。默认 undo 表空间的数据文件命名为 undo_001undo_002。定义在数据字典中的相应的 Undo 表空间名为 innodb_undo_001innodb_undo_002

MySQL 8.0.14 以后,可以使用 SQL 在运行时创建额外的 Undo 表空间。

Undo 表空间大小

MySQL 8.0.23 以前,Undo 表空间文件的初始大小取决于 innodb_page_size 值。对默认 16KB 页大小,Undo 表空间文件的初始大小是 10MiB。对于 4KB、8KB、32K 和 64KB 页大小,Undo 表空间文件的初始大小对应为 7MiB、8MiB、20MiB 和 40MiB。MySQL 8.0.23 以后,Undo 表空间文件的初始大小通常为 16MiB。当一个 Undo 表空间是由一个截断操作创建的时,初始大小可能不同。此时,如果文件扩展大小超过 16MB ,前一次文件扩展发生于上一秒内,新的 Undo 表空间以 innodb_max_undo_log_size 变量定义的大小的 1/4 为大小创建。

MySQL 8.0.23 以前,Undo 表空间一次扩展 4 个区(Extent)*。MySQL 8.0.23 以后,最小扩展 16MB 。为了应对快速增长,如果前一次文件扩展发生在 0.1 秒内,则本次进行双倍扩展。双倍扩展可以发生多次,直至达到最大值 256MB 。如果前一次文件扩展发生在超过 0.1 秒内,扩展大小减半,也可以发生多次,直至达到最小值 16MB 。如果为 Undo 表空间定义了AUTOEXTEND_SIZE 选项,则扩展大小取AUTOEXTEND_SIZE 的设置和以上逻辑描述决定的扩展大小二者中的较大者。

添加 Undo 表空间

由于 Undo 日志会在长时间运行的事务中变得很大,创建额外的 Undo 表空间可以避免单个表空间变得太大。MySQL 8.0.14 后,可以在运行时使用CREATE UNDO TABLESPACE语法创建额外表空间。

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

Undo 表空间必须使用 .ibu 扩展。定义 Undo 表空间文件名时不允许使用相对路径。允许使用全限定路径,但路径必须被 InnoDB 知晓。已知的路径由 innodb_directories 变量定义。建议使用唯一的 Undo 表空间文件名以防止在移动或克隆数据时遇到潜在的文件名冲突。

注意
在复制场景中,源和每个副本必须有它们自己的 Undo 表空间数据文件目录。复制 Undo 表空间数据文件的创建到公共目录会导致文件名冲突。

在启动时,扫描 innodb_directories 变量定义的目录以获取 Undo 表空间文件。(扫描还会遍历子目录)由 innodb_data_home_dirinnodb_undo_directory ,和 datadir 变量定义的目录自动追加到 innodb_directories 值后,而不管是否显式地定义了 innodb_directories 变量。因此,一个 Undo 表空间可能位于这些变量所定义的l路径下。

注意
InnoDB 恢复过程要求 Undo 表空间数据文件位于已知目录下。Undo 表空间数据文件必须在 redo 恢复和其他数据文件被打开以允许未提交的事务和数据字典修改可以回滚前,被发现并打开。无法使用在恢复前未被发现的 Undo 表空间数据文件,这可能导致数据库不一致。如果数据字典已知的 Undo 表空间数据文件在启动时未被发现,会报告错误信息。已知目录的要求还支持 Undo 表空间的可移植性。

查询INFORMATION_SCHEMA.FILES以查看 Undo 表空间名称和路径。

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

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

说明
MySQL 8.0.14 以前,通过配置 innodb_undo_tablespaces 启动变量来创建额外的 Undo 表空间,该变量在 MySQL 8.0.14 以后弃用且不可配置。
MySQL 8.0.14 以前,增加 innodb_undo_tablespaces 设置会创建指定数量的 Undo 表空间,并将它们添加到活动的 Undo 表空间列表中。反之,减少 innodb_undo_tablespaces 设置则将它们从活动的 Undo 表空间列表移除。从 活动的 Undo 表空间列表中移除的 Undo 表空间仍保持活跃状态直至它们不再被已存的事务需要。innodb_undo_tablespaces 变量可以由SET语句在运行时配置或在配置文件中定义。
MySQL 8.0.14 以前,不活动的 Undo 表空间无法被移除。在慢关机后可以手动移除 Undo 表空间,但不建议。因为如果在关闭服务器时存在打开的事务,则在服务器重新启动后的一段时间内,不活动的 Undo 表空间可能包含活动的 Undo 日志。 MySQL 8.0.14 以前,可以使用DROP UNDO TABALESPACE语法删除 Undo 表空间。

删除 Undo 表空间

MySQL 8.0.14 以后,使用CREATE UNDO TABLESPACE语法创建的 Undo 表空间可以在运行时使用DROP UNDO TABALESPACE语法删除。

Undo 表空间在被删除前必须为空。为了清空 Undo 表空间,首先必须使用ALTER UNDO TABLESPACE语法将其标记为非活跃状态,以使为新事务分配回滚段时不会使用该表空间。

ALTER UNDO TABLESPACE tablespace_name SET INACTIVE;

在 Undo 表空间被标记为非活跃状态后,允许当前使用 Undo 表空间中回滚段的事务以及任何在这些事务之前的事务完成。事务完成后,清除系统释放 Undo 表空间中的回滚段,Undo 表空间被截断为初始大小。(截断 Undo 表空间时使用同样的步骤)一旦 Undo 表空间为空就可以截断它。

DROP UNDO TABLESPACE tablespace_name;

说明
可选地,可以将 Undo 表空间保留为空状态,如果需要,以后通过ALTER UNDO TABLESPACE tablespace_name SET ACTIVE语句再次激活。

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

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

inactive 状态表示 Undo 表空间中的回滚段不再被新事务使用。empty 状态表示 Undo 表空间为空并准备好被删除,或准备好通过ALTER UNDO TABLESPACE tablespace_name SET ACTIVE语句再次激活。尝试删除非空的 Undo 表空间会返回错误。

在 MySQL 实例初始化时创建的默认 Undo 表空间(innodb_undo_001innodb_undo_002)无法被删除,但可以通过ALTER UNDO TABLESPACE tablespace_name SET ACTIVE语句使其失效。在 Undo 表空间失效以前,必须有一个可以替代的 Undo 表空间。任何时候都至少需要两个 Undo 表空间来支持 Undo 表空间的自动截断。

移动 Undo 表空间

在服务器离线时可以使用CREATE UNDO TABLESPACE语法移动 Undo 表空间到任何已知目录。移动到已知目录的任何 Undo 表空间文件会在启动时被发现,并假定为被移动的 Undo 表空间。

在 MySQL 实例初始化时创建的默认 Undo 表空间(innodb_undo_001innodb_undo_002)必须位于 innodb_undo_directory 所定义的目录下。如果未定义 innodb_undo_directory ,则使用数据目录。如果在服务器离线时移动默认 Undo 表空间,必须使用配置到新目录的 innodb_undo_directory 变量启动。

Undo 日志的 I/O 模式让SSD存储成为 Undo 表空间的良选。

配置回滚段的数量

innodb_rollback_segments 定义分配给每个 Undo 表空间和全局临时表空间的回滚段数量。可以在启动时或运行时定义它。它的默认值为 128 ,同时也是最大值。

截断 Undo 表空间

有两种截断 Undo 表空间的方式,可以单独或结合使用它们来管理 Undo 表空间的大小。一种是自动的,使用可配置的变量启动;另一种是手动的,使用 SQL 语句执行.

自动方式一旦启用,不需要监视 Undo 表空间的大小,它执行 Undo 表空间的停用、截断、重新激活而无需人工干预。如果你想在离线时控制截断,首选使用手动截断方式。例如,你可能想要在高峰时段避免截断 Undo 表空间。

自动截断

自动截断 Undo 表空间至少需要两个活跃的表空间,这样可以确保在一个 Undo表空间为了截断而离线时,另一个 Undo 表空间仍保持活跃。

启用 innodb_undo_log_truncate 变量来启用自动截断 Undo 表空间,例如:

mysql> SET GLOBAL innodb_undo_log_truncate=ON;

当启用 innodb_undo_log_truncate 变量时,禁止截断超过 innodb_max_undo_log_size 变量定义的大小限制的 Undo 表空间。innodb_max_undo_log_size 变量是动态的,默认值为 1073741824 字节(1024MB)。

mysql> SELECT @@innodb_max_undo_log_size;
+----------------------------+
| @@innodb_max_undo_log_size |
+----------------------------+
|                 1073741824 |
+----------------------------+

innodb_undo_log_truncate 变量被启用时:

  1. 超过 innodb_max_undo_log_size 变量定义的大小限制的默认的和用户定义的 Undo 表空间被标记为截断。使用循环的方式选择要截断的 Undo 表空间,以避免每次截断同一个。
  2. 位于选择的 Undo 表空间中的回滚段被置为无效,以使不会将它们分配给新事务。使用这些回滚段的当前已存在的事务允许继续执行完成。
  3. 清除(purge)系统通过释放不再使用的 Undo 日志来清空回滚段。
  4. 在 Undo 表空间中的所有回滚段均被释放后,截断操作运行并截断 Undo 表空间到初始大小。
    在截断操作完成后立即使用会导致截断后的 Undo 表空间大小大于初始大小。
  5. 重新激活回滚段以分配给新事务。
手动截断

手动截断至少需要三个活动的 Undo 表空间。总是需要两个 Undo 表空间来支持启用自动截断的可能性。最少需要三个满足允许一个表空间手动离线的要求。

手动初始化截断 Undo 表空间,执行如下语句使 Undo 表空间失效:

ALTER UNDO TABLESPACE tablespace_name SET INACTIVE;

在清除过程将 Undo 表空间截断为初始大小后,Undo 表空间的状态由 inactive 变为 empty

注意*
ALTER UNDO TABLESPACE tablespace_name SET INACTIVE语句停用一个 Undo 表空间时,清除线程会在下一个机会查找该 Undo 表空间。一旦找到 Undo 表空间并将其标记为截断后,清除线程会以更高的频率返回,以快速清空和截断还原表空间。

查询INFORMATION_SCHEMA.INNODB_TABLESPACES表来检查 Undo 表空间的状态:

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

一旦 Undo 表空间处于 empty 状态,可以执行如下语句再次激活:

ALTER UNDO TABLESPACE tablespace_name SET ACTIVE;

Undo 表空间处于 empty 状态时,也可以删除它。

加快 Undo 表空间的自动截断

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

mysql> SELECT @@innodb_purge_rseg_truncate_frequency;
+----------------------------------------+
| @@innodb_purge_rseg_truncate_frequency |
+----------------------------------------+
|                                    128 |
+----------------------------------------+

为了增加频率,可以禁用 innodb_purge_rseg_truncate_frequency 设置。例如,要使清除线程每调用 32 次清除就查找 Undo 表空间,设置 innodb_purge_rseg_truncate_frequency 变量为 32 :

mysql> SET GLOBAL innodb_purge_rseg_truncate_frequency=32;
截断 Undo 表空间文件的性能影响

截断 Undo 表空间时位于其中的回滚段失效。位于其他 Undo 表空间的活动回滚段承担整个系统负载,可能导致轻微的性能下降。性能受影响的程度取决于许多因素:

  • Undo 表空间的数量
  • Undo 日志的数量
  • Undo 表空间的大小
  • I/O 子系统的速度
  • 存在的长运行事务
  • 系统负载

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

监控截断 Undo 表空间

MySQL 8.0.16 后,提供 undopurge 子系统计数器来监控 Undo 日志截断相关的后台活动。查询INFORMATION_SCHEMA.INNODB_METRICS表获取计数器名称和描述。

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

有关启用计数器和查询计数器数据的信息,请查看 Section 15.15.6, “InnoDB INFORMATION_SCHEMA Metrics Table” 。

Undo 表空间截断的限制

MySQL 8.0.21 以后,在检查点间的同一 Undo 表空间上的截断操作数量被限制为 64 。该限制防止了由过多的 Undo 表空间截断操作导致的潜在问题。比如,如果将 innodb_max_undo_log_size 在繁忙的系统中设置得过低会发生的问题。如果超过限制,Undo 表空间仍可处于失效状态,但直到下一次检查点才截断。MySQL 8.0.22 中该限制由 64 增至 50,000 。

Undo 表空间截断的恢复

Undo 表空间截断操作会在服务器日志目录中创建一个临时的 undo_space_number_trunc.log 文件。该日志目录由 innodb_log_group_home_dir 定义。如果在截断操作过程中出现系统故障,临时日志文件允许启动进程识别被截断的 Undo表空间并继续操作。

Undo 表空间状态变量

下列状态变量允许追踪 Undo 表空间、隐式 Undo 表空间(InnoDB 创建的)、显式 Undo 表空间(用户创建的)的总数,和活动 Undo 表空间的数量。

mysql> SHOW STATUS LIKE 'Innodb_undo_tablespaces%';
+----------------------------------+-------+
| Variable_name                    | Value |
+----------------------------------+-------+
| Innodb_undo_tablespaces_total    | 2     |
| Innodb_undo_tablespaces_implicit | 2     |
| Innodb_undo_tablespaces_explicit | 0     |
| Innodb_undo_tablespaces_active   | 2     |
+----------------------------------+-------+

15.6.3.5 临时表空间

InnoDB 使用会话临时表空间和一个全局临时表空间。

会话临时表空间

会话临时表空间存储用户创建的临时表,和当 InnoDB 被配置为磁盘内部临时表的存储引擎时优化器创建的内部临时表。MySQL 8.0.16 开始,磁盘内部临时表使用的存储引擎是 InnoDB 。(之前存储引擎由 internal_tmp_disk_storage_engine 决定。)

会话临时表空间是在第一次请求创建磁盘临时表时,从临时表空间池中分配给会话的。一个会话最多可以分配两个表空间,一个用于用户创建的临时表,另一个用于优化程序创建的内部临时表。分配给会话的临时表空间用于该会话创建的所有磁盘上的临时表。当会话断开连接时,其临时表空间会被截断并释放回池中。服务器启动时会创建一个包含 10 个临时表空间的池。池的大小永远不会缩小,并且表空间会根据需要自动添加到池中。在正常关闭或中止初始化时,临时表空间池将被删除。会话临时表空间文件在创建时有五页大小,并且有一个 .ibt 文件扩展名。

为会话临时表空间保留了 40 万个空间 ID。因为每次启动服务器时都会重新创建会话临时表空间池,所以当服务器关闭时,会话临时表空间的空间 ID 不会保留,而是可以重用。

innodb_temp_tablespaces_dir 变量定义了创建会话临时表空间的位置。默认位置是数据目录中的 #innodb_temp 目录。如果无法创建临时表空间池,将拒绝启动。

$> cd BASEDIR/data/#innodb_temp
$> ls
temp_10.ibt  temp_2.ibt  temp_4.ibt  temp_6.ibt  temp_8.ibt
temp_1.ibt   temp_3.ibt  temp_5.ibt  temp_7.ibt  temp_9.ibt

在基于语句的复制(SBR)模式中,在副本上创建的临时表位于在单个会话临时表空间中,该表空间仅在 MySQL 服务器关闭时被截断。

INNODB_SESSION_TEMP_TABLESPACES表提供了有关会话临时表空间的元数据。

INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO提供了用户创建的临时表的元数据,这些临时表在 InnoDB 实例中是活动的。

全局临时表空间

全局临时表空间(ibtmp1) 存储对用户创建的临时表所做更改的回退段。

innodb_temp_data_file_path 变量定义全局临时表空间数据文件的相对路径、名称、大小和属性。如果没有为 innodb_temp_data_file_path 指定值,默认行为是在 innodb_data_home_dir 目录中创建一个名为 ibtmp1 的自动扩展数据文件。初始文件大小略大于 12MB。

全局临时表空间在正常关闭或中止初始化时被删除,并在每次服务器启动时重新创建。全局临时表空间在创建时会收到一个动态生成的空间 ID。如果无法创建全局临时表空间,启动将被拒绝。如果服务器意外停止,全局临时表空间不会被删除。在这种情况下,数据库管理员可以手动删除全局临时表空间,或者重新启动 MySQL 服务器。重新启动 MySQL 服务器会自动删除并重新创建全局临时表空间。

全局临时表空间不能保存在裸设备上。

INFORMATION_SCHEMA.FILES提供了关于全局临时表空间的元数据。执行类似下面的查询来查看全局临时表空间元数据:

mysql> SELECT * FROM INFORMATION_SCHEMA.FILES WHERE TABLESPACE_NAME='innodb_temporary'\G

要确定全局临时表空间数据文件是否自动扩展,请检查 innodb_temp_data_file_path 设置:

mysql> SELECT @@innodb_temp_data_file_path;
+------------------------------+
| @@innodb_temp_data_file_path |
+------------------------------+
| ibtmp1:12M:autoextend        |
+------------------------------+

要检查全局临时表空间数据文件的大小,使用类似如下查询查看INFORMATION_SCHEMA.FILES表:

mysql> SELECT FILE_NAME, TABLESPACE_NAME, ENGINE, INITIAL_SIZE, TOTAL_EXTENTS*EXTENT_SIZE
       AS TotalSizeBytes, DATA_FREE, MAXIMUM_SIZE FROM INFORMATION_SCHEMA.FILES
       WHERE TABLESPACE_NAME = 'innodb_temporary'\G
*************************** 1. row ***************************
      FILE_NAME: ./ibtmp1
TABLESPACE_NAME: innodb_temporary
         ENGINE: InnoDB
   INITIAL_SIZE: 12582912
 TotalSizeBytes: 12582912
      DATA_FREE: 6291456
   MAXIMUM_SIZE: NULL

TotalSizeBytes 显示全局临时表空间数据文件的当前大小。

或者,检查操作系统上的全局临时表空间数据文件大小。全局临时表空间数据文件位于由 innodb_temp_data_file_path 变量定义的目录中。

要回收全局临时表空间数据文件占用的磁盘空间,请重新启动 MySQL 服务器。重新启动服务器会根据 innodb_temp_data_file_path 定义的属性删除并重新创建全局临时表空间数据文件。

要限制全局临时表空间数据文件的大小,请配置 innodb_temp_data_file_path 以指定最大文件大小。例如:

[mysqld]
innodb_temp_data_file_path=ibtmp1:12M:autoextend:max:500M

配置 innodb_temp_data_file_path 需要重启服务器。

15.6.3.6 在服务器离线时移动表空间

innodb_directories 变量定义了在启动时扫描表空间文件的目录,支持在服务器离线时将表空间文件移动或恢复到新位置。在启动过程中,将使用发现的表空间文件,而不是数据字典中引用的文件,并且更新数据字典以引用重新定位的文件。如果扫描发现重复的表空间文件,则启动失败,并显示一个错误,表明为同一个表空间 ID 找到了多个文件。

innodb_data_home_dirinnodb_undo_directorydatadir 变量定义的目录将自动追加到 innodb_directories 参数值中。无论是否明确指定了 innodb_directories 设置,都会在启动时扫描这些目录。这些目录的隐式添加允许在不配置 innodb_directories 设置的情况下移动系统表空间文件、数据目录或 Undo 表空间文件。但是,目录更改时必须更新设置。例如,重新定位数据目录后,必须在重新启动服务器之前更新--datadir设置。

innodb_directories 变量可以在启动命令或 MySQL 选项文件中指定。参数值使用引号包围,因为分号(;)被一些命令解释器解释为特殊字符。(例如,Unix shell将其视为命令终止符。)

启动命令:

mysqld --innodb-directories="directory_path_1;directory_path_2"

MySQL 选项文件:

[mysqld]
innodb_directories="directory_path_1;directory_path_2"

下列步骤应用于移动单独的单表表空间、普通表空间数据文件、系统表空间数据文件、Undo 表空间数据文件,或数据目录。在移动文件或目录前,审阅如下使用说明:

  1. 停止服务器。
  2. 移动表空间文件或目录到想要的位置。
  3. 使新目录对 InnoDB 已知。
    • 如果移动单独的单表表空间或普通表空间文件,添加未知目录到 innodb_directories 值中。
      • innodb_data_home_dirinnodb_undo_directorydatadir 变量定义的目录将自动追加到 innodb_directories 参数值中,所以你不需要指定他们。
      • 单表表空间文件只能移动到与模式同名的目录中。例如,如果 actor 表属于 sakila 模式,那么 actor.ibd 数据文件只能移动到名为 sakila 的目录中。
      • 普通表空间文件不能移动到数据目录或其子目录。
    • 如果移动系统表空间文件、Undo 表空间文件或数据目录,根据需要更新 innodb_data_home_dirinnodb_undo_directorydatadir
  4. 重启服务器。

使用说明

  • 通配符表达式不可用于 innodb_directories 参数值。
  • innodb_directories 扫描也遍历指定目录的子目录。重复的目录和子目录从要扫描的目录列表中丢弃。
  • innodb_directories 支持移动 InnoDB 表空间文件。不支持属于除 InnoDB 以外存储引擎的文件。该限制也适用于移动整个数据目录。
  • innodb_directories 支持当移动文件到已扫描的目录时重命名表空间文件,也支持移动表空间文件到其他支持的操作系统。
  • 将表空间文件移动到不同的操作系统时,请确保表空间文件名不包含禁用字符或在目标系统上有特殊含义的字符。
  • 将数据目录从 Windows 操作系统移动到 Linux 操作系统时,请修改二进制日志索引文件中的二进制日志文件路径,以使用反斜线而不是正斜线。默认情况下,二进制日志索引文件与二进制日志文件具有相同的基本名称,扩展名为“.index”。二进制日志索引文件的位置由--log-bin定义。默认位置是数据目录。
  • 如果将表空间文件移动到不同的操作系统会引入跨平台复制,则数据库管理员有责任确保正确复制包含平台特定的目录的DDL语句。允许指定目录的语句包括CREATE TABLE ... DATA DIRECTORYCREATE TABLESPACE … ADD DATAFILE
  • 将使用绝对路径或在数据目录之外的位置创建的单表或常规表空间的目录添加到 innodb_directories 设置中。否则,InnoDB无法在恢复期间找到文件。有关相关信息,请参阅崩溃恢复期间的表空间发现。

要查看表空间文件位置,请查询INFORMATION_SCHEMA.FILES表:

mysql> SELECT TABLESPACE_NAME, FILE_NAME FROM INFORMATION_SCHEMA.FILES \G

15.6.3.7 禁用表空间路径验证

启动时,InnoDB 扫描由 innodb_directories 变量定义的目录以查找表空间文件。根据数据字典中记录的路径验证发现的表空间文件的路径。如果路径不匹配,则更新数据字典中的路径。
MySQL 8.0.21 中引入的 innodb_validate_tablespace_paths 变量允许禁用表空间路径验证。此功能适用于未移动表空间文件的环境。禁用路径验证可以缩短具有大量表空间文件的系统的启动时间。如果 log_error_verbosity 设置为 3,则当禁用表空间路径验证时,将在启动时打印以下消息:

[InnoDB] Skipping InnoDB tablespace path validation. 
Manually moved tablespace files will not be detected!

警告
移动表空间文件后,在禁用表空间路径验证的情况下启动服务器可能会导致未定义的行为。

15.6.3.8 优化在 Linux 上的表空间分配

从 MySQL 8.0.22 开始,您可以优化 InnoDB 在 Linux 上为单表和普通表空间分配空间的方式。默认情况下,当需要额外的空间时,InnoDB 将页面分配给表空间,并将 NULL 物理写入这些页面。如果频繁分配新页面,此行为可能会影响性能。从 MySQL 8.0.22 开始,您可以在 Linux 系统上禁用 innodb_extend_and_initialize ,以避免将 NULL 物理写入新分配的表空间页面。当 innodb_extend_and_initialize 被禁用时,使用 posix_fallocate() 调用将空间分配给表空间文件,该调用保留空间而不实际写入 NULL 。
当使用 posix_fallocate() 调用分配页面时,默认情况下扩展大小很小,一次通常只分配几个页面,这会导致碎片化并增加随机I/O。为避免此问题,请在启用 posix_fallocate() 调用时增加表空间扩展大小。使用AUTOEXTEND_SIZE选项,表空间扩展大小可以增加到4GB。
InnoDB 在分配新的表空间页面之前写入重做日志记录。如果页面分配操作被中断,则在恢复期间将从重做日志记录中重播该操作。(从重做日志记录重放的页面分配操作将 NULL 物理写入新分配的页面。)无论 innodb_extend_and_initialize 设置如何,在分配页面之前都会写入重做日志。

在非 Linux 系统和 Windows 上,InnoDB 为表空间分配新页面,并将 NULL 物理写入这些页面,这是默认行为。尝试在这些系统上禁用 innodb_extend_and_initialize 会返回以下错误:

Changing innodb_extend_and_initialize not supported on this platform. Falling back to the default.

15.6.3.9 表空间 AUTOEXTEND_SIZE 配置

默认情况下,当单表或普通表空间的文件需要额外空间时,表空间会根据以下规则进行增量扩展:

  • 如果表空间的大小小于一个区,则一次扩展一页。
  • 如果表空间大于1个区但小于32个区,则一次扩展一个区。
  • 如果表空间的大小超过32个区,则一次扩展四个区。

有关数据块大小的信息,请参阅 第15.11.2节 “文件空间管理”。

在 MySQL 8.0.23 中,通过指定AUTOEXTEND_SIZE选项,可以配置单表或普通表空间的文件扩展量。配置更大的扩展大小有助于避免碎片化,并有助于摄取大量数据。
要为单表空间配置文件的扩展名大小,请在CREATE TABLEALTER TABLE语句中指定AUTOEXTEND_SIZE大小:

CREATE TABLE t1 (c1 INT) AUTOEXTEND_SIZE = 4M;
ALTER TABLE t1 AUTOEXTEND_SIZE = 8M;

要配置普通表空间的扩展大小,请在CREATE TABLESPACEALTER TABLESPACE语句中指定AUTOEXTEND_SIZE大小:

CREATE TABLESPACE ts1 AUTOEXTEND_SIZE = 4M;
ALTER TABLESPACE ts1 AUTOEXTEND_SIZE = 8M;

说明
创建 Undo 表空间时,也可以使用AUTOEXTEND_SIZE选项,但 Undo 表空间的扩展行为有所不同。有关详细信息,请参阅 第 15.6.3.4 “还原表空间”节。

AUTOEXTEND_SIZE设置必须是4M的倍数。指定不是4M倍数的AUTOEXTEND_SIZE设置将返回错误。
AUTOEXTEND_SIZE默认设置为0,这会导致表空间根据上述默认行为进行扩展。
在 MySQL 8.0.23 中,最大AUTOEXTEND_SIZE置为64M。从 MySQL 8.0.34 中,最大设置为4GB。
最小AUTOEXTEND_SIZE设置取决于 InnoDB 页面大小,如下表所示:

InnoDB 页大小 AUTOEXTEND_SIZE 最小值
4K 4M
8K 4M
16K 4M
32K 8M
64K 16M

InnoDB 页大小默认为 16KB(16384 bytes)。查询 innodb_page_size 设置以确定你的 MySQL 实例的页大小:

mysql> SELECT @@GLOBAL.innodb_page_size;
+---------------------------+
| @@GLOBAL.innodb_page_size |
+---------------------------+
|                     16384 |
+---------------------------+

当表空间的AUTOEXTEND_SIZE设置更改时,随后发生的第一个扩展将表空间大小增加到AUTOEXTEND_SIZE设置的倍数。后续扩展使用配置的大小。
当使用非零AUTOEXTEND_SIZE设置创建单表或普通表空间的文件时,表空间将以指定的AUTOEXTEND_SIZE大小初始化。
ALTER TABLESPACE不能用于为单表空间配置文件的AUTOEXTEND_SIZE。必须使用ALTER TABLE
对于创建于单表表空间的表,仅当AUTOEXTEND_SIZE选项配置为非零值时,SHOW CREATE TABLE才会显示该选项。
要确定任何 InnoDB 表空间的AUTOEXTEND_SIZE,请查询INFORMATION_SCHEMA.INNODB_TABLESPACES表。例如:

mysql> SELECT NAME, AUTOEXTEND_SIZE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES 
       WHERE NAME LIKE 'test/t1';
+---------+-----------------+
| NAME    | AUTOEXTEND_SIZE |
+---------+-----------------+
| test/t1 |         4194304 |
+---------+-----------------+

mysql> SELECT NAME, AUTOEXTEND_SIZE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES 
       WHERE NAME LIKE 'ts1';
+------+-----------------+
| NAME | AUTOEXTEND_SIZE |
+------+-----------------+
| ts1  |         4194304 |
+------+-----------------+

说明
AUTOEXTEND_SIZE为 0 ,这是默认设置,意味着表空间根据上述默认表空间扩展行为进行扩展。

15.6.4 双写缓冲区(Doublewrite Buffer)

双写缓冲区是 InnoDB 在将页面写入 InnoDB 数据文件中的正确位置之前,将页面从缓冲池中刷新到的一个存储区域。如果在页面写入过程中存在操作系统、存储子系统或意外的 mysqld 进程退出,InnoDB 可以在崩溃恢复期间从双写缓冲区中找到页面的完好副本。

尽管数据写入两次,但双写缓冲区不需要两倍的 I/O 开销或两倍的 I/O 操作。通过对操作系统的一次 fsync() 调用,数据以大的顺序块写入双写缓冲区(innodb_flush_method 设置为O_DIRECT_NO_FSYNC的情况除外)。

在 MySQL 8.0.20 之前,双写缓冲区存储区域位于 InnoDB 系统表空间中。从 MySQL 8.0.20 开始,双写缓冲存储区位于双写文件中。

为双写缓冲区配置提供了以下变量:

  • innodb_doublewrite
    该变量控制是否启用双写缓冲区。在大多数情况下,它默认启用。要禁用双写缓冲区,请将 innodb_doublewrite 设置为OFF。如果您更关心性能而不是数据完整性,请考虑禁用双写缓冲器,例如,在执行基准测试时。
    从 MySQL 8.0.30 开始,innodb_doublewrite 支持DETECT_AND_RECOVERDETECT_ONLY设置。
    DETECT_AND_RECOVER设置与ON设置相同。使用此设置,双写缓冲区将完全启用,数据库页面内容将写入双写缓冲,在恢复过程中可以访问双写缓冲以修复不完整的页面写入。
    使用DETECT_ONLY设置,只有元数据写入双写缓冲区。数据库页面内容不会写入双写缓冲区,恢复不会使用双写缓冲来修复不完整的页面写入。此轻量级设置仅用于检测不完整的页面写入。
    MySQL 8.0.30 以后的版本支持允许在ONDETECT_AND_RECOVERDETECT_ONLY之间,动态更改已启用双写缓冲区的 innodb_doublewrite 设置。MySQL 不支持在启用双写缓冲区的设置和关闭之间进行动态更改,反之亦然。
    如果双写缓冲区位于支持原子写入的**聚合I/O(Fusion-io)**设备上,则会自动禁用双写缓冲区,并使用 Fusion-io 原子写入来执行数据文件写入。但是,请注意 innodb_doublewrite 设置是全局的。当双写缓冲区被禁用时,它将对所有数据文件禁用,包括那些不位于 Fusion-io 硬件上的数据文件。此功能仅在 Fusion-io 硬件上受支持,仅在 Linux 上的 Fusion-io NVMFS 上启用。为了充分利用这一特性,建议将 innodb_flush_method 设置为O_DIRECT

  • innodb_doublewrite_dir
    该变量在 MySQL 8.0.20 引入,定义 InnoDB 创建双写文件的目录。如果未指定目录,双写文件创建于 innodb_data_home_dir ,该目录是未指定时的默认数据目录。
    哈希符号“#”将自动作为指定目录名的前缀,以避免与模式名称冲突。但是,如果在目录名中显式指定了“.”、“#”、“/”前缀,则哈希符号“#”不作为目录名的前缀。
    理想情况下,双写目录应该放在可用的最快存储介质上。

  • innodb_doublewrite_files
    该变量定义了双写文件的数量。默认情况下,为每个缓冲池实例创建两个双写文件:一个刷新列表双写文件和一个 LRU 列表双写。
    刷新列表双写文件用于从缓冲池刷新列表中刷新的页面。刷新列表双写文件的默认大小为 InnoDB 页面大小 * 双写页面字节数
    LRU 列表双写文件用于从缓冲池 LRU 列表中清除的页面。它还包含用于单页刷新的插槽。LRU列表双写文件的默认大小为 InnoDB页面大小 * (双写页数 + (512 / 缓冲池实例数)),其中 512 是为单页刷新保留的插槽总数。
    至少有两个双写文件。双写文件的最大数量是缓冲池实例数量的两倍。(缓冲池实例的数量由 innodb_buffer_pool_instances 变量控制。)
    双写文件名的格式如下: #ib_page_size_file_number.dblwr (或带有DETECT_ONLY设置的 .bdblwr)。例如,为 InnoDB 页面大小为 16KB 且具有单个缓冲池的 MySQL 实例创建以下双写文件:

#ib_16384_0.dblwr
#ib_16384_1.dblwr

innodb_doublewrite_files 变量用于高级性能优化。默认配置应该适合大多数用户。

  • innodb_doublewrite_pages
    该变量在 MySQL 8.0.20 引入,控制每个线程的最大双写页面数。如果未指定值,则将 innodb_doublewrite_pages 被设置为 innodb_write_io_threads 值。此变量用于高级性能优化。默认值应该适合大多数用户。
  • innodb_doublewrite_batch_size
    该变量在 MySQL 8.0.20 引入,控制批量写入的双写页面数。此变量用于高级性能优化。默认值应该适合大多数用户。

从 MySQL 8.0.23 开始,InnoDB 自动加密属于加密表空间的双写文件页(参见 第15.13节 “InnoDB静态数据加密”)。同样,属于页压缩表空间的双写文件页也会被压缩。因此,双写文件可以包含不同的页面类型,包括未加密且未压缩页面、加密页面、压缩页面以及加密且压缩页面。

15.6.5 重做日志(Redo Logs)

重做日志是一种基于磁盘的数据结构,在崩溃恢复期间用于修正由不完整事务写入的数据。在正常操作期间,重做日志对 SQL 语句或低级 API 调用产生的更改表数据的请求进行编码。在意外关机之前未完成更新数据文件的修改将在初始化期间和接受连接之前自动重放。有关重做日志在崩溃恢复中的作用的信息,请参阅 第15.18.2节 “InnoDB 恢复”。

重做日志在磁盘上由重做日志文件物理表示。写入重做日志文件的数据按照受影响的记录进行编码,这些数据统称为重做(Redo)。贯穿重做日志文件的数据片段由不断增加的 LSN 值表示。重做日志数据会随着数据修改而追加,最旧的数据会随着检查点的前进而被截断。

配置重做日志容量(MySQL 8.0.30 及以后)

从 MySQL 8.0.30 开始,innodb_redo_log_capacity 控制重做日志所占用的磁盘空间总额。你可以在启动时在选项文件中设置它,或在运行时使用SET GLOBAL语句;例如,下列语句将重做日志容量设置为 8GB :

SET GLOBAL innodb_redo_log_capacity = 8589934592;

当在运行时设置时,配置更改会立即发生,但新的限制可能需要一段时间才能完全实现。如果重做日志文件占用的空间小于指定值,则脏页会从缓冲池以较低的积极性刷新到表空间数据文件,最终会增加重做日志所占用的磁盘空间。如果重做日志文件占用的空间超过指定值,则会更积极地刷新脏页,最终减少重做日志所占用的磁盘空间。

innodb_redo_log_capacity 变量取代了已弃用的 innodb_ log_files_in_groupinnodb_log_file_size 变量。innodb_redo_log_capacity 设置已被定义时,将忽略 innodb_log_files_in_groupinnodb_ log_file_size 设置;否则,这些设置将用于计算 innodb_redo_log_capacity 设置(innodb_log_files_in_group * innodb_log_file_size = innodb_redo_log_capacity)。如果没有设置这些变量,则重做日志容量将设置为 innodb_redo_log_capacity 默认值,即 104857600 字节(100MB)。最大重做日志容量为 128GB

除非 innodb_log_group_home_dir 变量指定了不同的目录,否则重做日志文件位于数据目录的 #innodb_redo 目录中。如果定义了 innodb_log_group_home_dir ,则重做日志文件位于该目录中的 #innodb_redo 目录中。有两种类型的重做日志文件,普通和备用。普通的重做日志文件就是正在使用的那些文件。备用重做日志文件是那些等待使用的文件。InnoDB 尝试维护总共 32 个重做日志文件,每个文件的大小等于 1/32 * innodb_redo_log_capacity ;然而,在修改 *** innodb_redo_log_capacity*** 设置后,文件大小可能会有一段时间的不同。
重做日志文件使用 #ib_redoN 命名规则,其中 N 是重做日志的文件号。备用重做日志文件由 _tmp 后缀表示。下面的示例显示了 #innodb_redo 目录中的重做日志文件,其中有 21 个活动重做日志和 11 个备用重做日志,按顺序编号。

'#ib_redo582'  '#ib_redo590'  '#ib_redo598'      '#ib_redo606_tmp'
'#ib_redo583'  '#ib_redo591'  '#ib_redo599'      '#ib_redo607_tmp'
'#ib_redo584'  '#ib_redo592'  '#ib_redo600'      '#ib_redo608_tmp'
'#ib_redo585'  '#ib_redo593'  '#ib_redo601'      '#ib_redo609_tmp'
'#ib_redo586'  '#ib_redo594'  '#ib_redo602'      '#ib_redo610_tmp'
'#ib_redo587'  '#ib_redo595'  '#ib_redo603_tmp'  '#ib_redo611_tmp'
'#ib_redo588'  '#ib_redo596'  '#ib_redo604_tmp'  '#ib_redo612_tmp'
'#ib_redo589'  '#ib_redo597'  '#ib_redo605_tmp'  '#ib_redo613_tmp'

每个普通重做日志文件都与特定范围的 LSN 值相关联;例如,以下查询显示了上一示例中列出的活动重做日志文件的START_LSNEND_LSN值:

mysql> SELECT FILE_NAME, START_LSN, END_LSN FROM performance_schema.innodb_redo_log_files;
+----------------------------+--------------+--------------+
| FILE_NAME                  | START_LSN    | END_LSN      |
+----------------------------+--------------+--------------+
| ./#innodb_redo/#ib_redo582 | 117654982144 | 117658256896 |
| ./#innodb_redo/#ib_redo583 | 117658256896 | 117661531648 |
| ./#innodb_redo/#ib_redo584 | 117661531648 | 117664806400 |
| ./#innodb_redo/#ib_redo585 | 117664806400 | 117668081152 |
| ./#innodb_redo/#ib_redo586 | 117668081152 | 117671355904 |
| ./#innodb_redo/#ib_redo587 | 117671355904 | 117674630656 |
| ./#innodb_redo/#ib_redo588 | 117674630656 | 117677905408 |
| ./#innodb_redo/#ib_redo589 | 117677905408 | 117681180160 |
| ./#innodb_redo/#ib_redo590 | 117681180160 | 117684454912 |
| ./#innodb_redo/#ib_redo591 | 117684454912 | 117687729664 |
| ./#innodb_redo/#ib_redo592 | 117687729664 | 117691004416 |
| ./#innodb_redo/#ib_redo593 | 117691004416 | 117694279168 |
| ./#innodb_redo/#ib_redo594 | 117694279168 | 117697553920 |
| ./#innodb_redo/#ib_redo595 | 117697553920 | 117700828672 |
| ./#innodb_redo/#ib_redo596 | 117700828672 | 117704103424 |
| ./#innodb_redo/#ib_redo597 | 117704103424 | 117707378176 |
| ./#innodb_redo/#ib_redo598 | 117707378176 | 117710652928 |
| ./#innodb_redo/#ib_redo599 | 117710652928 | 117713927680 |
| ./#innodb_redo/#ib_redo600 | 117713927680 | 117717202432 |
| ./#innodb_redo/#ib_redo601 | 117717202432 | 117720477184 |
| ./#innodb_redo/#ib_redo602 | 117720477184 | 117723751936 |
+----------------------------+--------------+--------------+

执行检查点时,InnoDB 将检查点 LSN 存储在包含该 LSN 的文件的头中。在恢复期间,将检查所有重做日志文件,并从最新的检查点 LSN 开始恢复。

提供了几个状态变量用于监视重做日志和重做日志容量调整操作;例如,可以查询 Innodb_redo_log_resize_status(注意该变量首字母大写) 以查看调整大小操作的状态:

mysql> SHOW STATUS LIKE 'Innodb_redo_log_resize_status';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Innodb_redo_log_resize_status | OK    |
+-------------------------------+-------+

Innodb_redo_log_capacity_resized 显示当前重做日志容量限制:

mysql> SHOW STATUS LIKE 'Innodb_redo_log_capacity_resized';
 +----------------------------------+-----------+
| Variable_name                    | Value     |
+----------------------------------+-----------+
| Innodb_redo_log_capacity_resized | 104857600 |
+----------------------------------+-----------+

其他适用的状态变量包括:

  • Innodb_redo_log_checkpoint_lsn
  • Innodb_redo_log_current_lsn
  • Innodb_redo_log_flushed_to_disk_lsn
  • Innodb_redo_log_logical_size
  • Innodb_redo_log_physical_size
  • Innodb_redo_log_read_only
  • Innodb_redo_log_uuid

有关详细信息,请参阅状态变量说明。

您可以通过查询 innodb_redo_log_files 性能模式(Performance Schema) 表来查看有关活动重做日志文件的信息。以下查询从表的所有列中检索数据:

SELECT FILE_ID, START_LSN, END_LSN, SIZE_IN_BYTES, IS_FULL, CONSUMER_LEVEL 
FROM performance_schema.innodb_redo_log_files;

配置重做日志容量(MySQL 8.0.30 以前)

在 MySQL 8.0.30 之前,InnoDB 默认在数据目录中创建两个重做日志文件,分别名为 ib_logfile0ib_logfile1 ,并以循环方式写入这些文件。

修改重做日志容量需要更改重做日志文件的数量或大小,或同时更改两者。

  1. 停止 MySQL 服务器并确保其关闭时没有错误。
  2. 编辑 my.cnf 更改重做日志文件配置。要更改重做日志文件大小,请配置 innodb_log_file_size 。要增加重做日志文件的数量,请配置 innodb_log_files_in_group
  3. 重新启动 MySQL 服务器。

如果 InnoDB 检测到 innodb_log_file_size 与重做日志文件大小不同,它将写入一个日志检查点,关闭并删除旧日志文件,以请求的大小创建新日志文件,并打开新日志文件。

自动配置重做日志容量

启用 innodb_dedicated_server 时,InnoDB 会自动配置某些 InnoDB 参数,包括重做日志容量。自动配置适用于位于 MySQL 专属的服务器上的 MySQL 实例,其中 MySQL 服务器可以使用所有可用的系统资源。有关更多信息,请参见 第15.8.12节 “为专用MySQL Server启用自动配置”。

归档重做日志

在备份操作进行时,复制重做日志记录的备份实用程序有时可能无法跟上重做日志生成的速度,从而导致由于这些记录被覆盖而丢失重做日志。当备份操作期间 MySQL 服务器有大量活动,并且重做日志文件存储介质的运行速度比备份存储介质快时,通常会出现此问题。MySQL 8.0.17 中引入的重做日志归档功能通过将重做日志记录顺序写入归档文件以及重做日志文件来解决这个问题。备份实用程序可以根据需要从存档文件中复制重做日志记录,从而避免潜在的数据丢失。

如果在服务器上配置了重做日志存档,则 MySQL Enterprise Edition 提供的 MySQL Enterprise Backup 在备份 MySQL 服务器时使用重做日志归档功能。

在服务器上启用重做日志归档需要设置 innodb_redo_log_archive_dirs 系统变量的值。该值指定为以分号分隔的标签化的重做日志存档目录列表。标签:目录(label:directory) 对由冒号(:)分隔。例如:

mysql> SET GLOBAL innodb_redo_log_archive_dirs='label1:directory_path1[;label2:directory_path2;…]';

标签(label)是代表存档目录的任意标识符。它可以是任何字符串,冒号(:)除外,这是不允许的。也允许使用空标签,但在这种情况下仍然需要冒号(:)。必须指定 directory_path 。激活重做日志归档时,为重做日志存档文件选择的目录必须存在,否则返回错误。路径可以包含冒号(“:”),但不允许包含分号(;)。

必须配置 innodb_redo_log_archive_dirs 变量,才能激活重做日志归档。默认值为NULL,不允许激活重做日志归档。

说明
指定的存档目录必须满足以下要求。(当激活重做日志归档时,将强制执行这些要求。):

  • 目录必须存在。重做日志存档进程不会创建目录。否则,将返回以下错误:
ERROR 3844 (HY000): Redo log archive directory 'directory_path1' does not exist or is not a directory
  • 目录不能是全局可访问的。这是为了防止重做日志数据暴露给系统上未经授权的用户。否则,将返回以下错误:

ERROR 3846 (HY000): Redo log archive directory ‘directory_path1’ is accessible to all OS users

- 目录不能是由 ***datadir*** 、***innodb_data_home_dir*** 、***innodb_directories*** 、***innodb_log_group_home_dir*** 、***innodb_temp_tablespaces_dir*** 、***innodb_tmpdir*** 、***innodb_undo_directory*** 或 ***secure_file_priv*** 定义的目录,也不能是这些目录的父目录或子目录。否则,将返回类似以下的错误:
```sql
ERROR 3845 (HY000): Redo log archive directory 'directory_path1' is in, under, or over server directory 'datadir' - '/path/to/data_directory'

当支持重做日志归档的备份实用程序启动备份时,备份实用程序通过调用 innodb_redo_log_archive_start() 函数激活重做日志存档。

如果您不使用支持重做日志存档的备份实用程序,也可以手动激活重做日志归档,如下所示:

mysql> SELECT innodb_redo_log_archive_start('label', 'subdir');
+------------------------------------------+
| innodb_redo_log_archive_start('label') |
+------------------------------------------+
| 0                                        |
+------------------------------------------+

或:

mysql> DO innodb_redo_log_archive_start('label', 'subdir');
Query OK, 0 rows affected (0.09 sec)

注意
激活重做日志归档的 MySQL 会话(使用 innodb_redo_log_archive_start() 函数)必须在归档期间保持打开。必须使用同一会话停用重做日志归档(使用 innodb_redo_log_archive_stop() )。如果会话在重做日志存档被显式停用之前终止,服务器将隐式停用重做日志归档并删除重做日志档案文件。

其中 label 是由 innodb_redo_log_archive_dirs 定义的标签;subdir是一个可选参数,用于指定标签标识的目录的子目录来保存存档文件;它必须是一个简单的目录名(不允许使用斜杠(/)、反斜杠(**)或冒号(*))。subdir 可以为空、null,也可以省略。

只有具有INNODB_REDO_LOG_ARCHIVE权限的用户才能通过调用 innodb_redo_log_archive_start() 来激活重做日志归档,或者使用 innodb_redo_log_archive_stop() 将其停用。运行备份实用程序的 MySQL 用户或手动激活和停用重做日志归档的 MySQL 客户必须具有此权限。

重做日志存档文件路径为directory_identified_by_label/[subdir/]archive.serverUUID.000001.log,其中 directory _identified_by_label 是由 innodb_redo_log_archive_start()label 参数标识的存档目录。subdir 是用于 innodb_redo_log_archive_start() 的可选参数。

例如,重做日志归档文件的完整路径和名称如下所示:

/directory_path/subdirectory/archive.e71a47dc-61f8-11e9-a3cb-080027154b4d.000001.log

备份实用程序完成复制 InnoDB 数据文件后,通过调用 innodb_redo_log_archive_stop() 函数停用重做日志归档。

如果您不使用支持重做日志存档的备份实用程序,也可以手动停用重做日志归档,如下所示:

mysql> SELECT innodb_redo_log_archive_stop();
+--------------------------------+
| innodb_redo_log_archive_stop() |
+--------------------------------+
| 0                              |
+--------------------------------+

或:

mysql> DO innodb_redo_log_archive_stop();
Query OK, 0 rows affected (0.01 sec)

停止函数成功完成后,备份实用程序从归档文件中查找重做日志数据的相关部分,并将其复制到备份中。

在备份实用程序完成复制重做日志数据并且不再需要重做日志存档文件后,它会删除该存档文件。

在正常情况下,备份实用程序负责删除归档文件。但是,如果在调用 innodb_redo_log_archive_stop() 之前重做日志归档操作意外退出,MySQL 服务器将删除该文件。

性能考虑因素

由于额外的写入活动,激活重做日志归档通常会带来轻微的性能成本。

在 Unix 和类 Unix 操作系统上,如果没有持续的高更新率,性能影响通常很小。相比之下,在 Windows 上,性能影响通常稍高一些。

如果存在持续的高更新率,并且重做日志存档文件与重做日志文件位于同一存储介质上,则由于混合的写入活动,性能影响可能更大。

如果存在持续的高更新率,并且重做日志存档文件位于比重做日志文件慢的存储介质上,则性能会受到任意影响。

写入重做日志归档文件不会妨碍正常的事务日志记录,除非重做日志存档文件存储介质的操作速度比重做日志文件存储介质慢得多,并且有大量的持久化重做日志块等待写入重做记录归档文件。在这种情况下,事务日志记录速率降低到可以由重做日志存档文件所在的较慢存储介质管理的水平。

禁用重做日志

从 MySQL 8.0.21 开始,可以使用ALTER INSTANCE DISABLE INNODB REDO_LOG语句禁用重做日志记录。此功能旨在将数据加载到新的 MySQL 实例中。禁用重做日志通过避免重做日志写入和双写缓冲来加快数据加载。

警告
此功能仅用于将数据加载到新的 MySQL 实例中。请勿在生产系统上禁用重做日志记录。允许在禁用重做日志记录时关闭并重新启动服务器,但在禁用重作日志记录时服务器意外停止可能会导致数据丢失和实例损坏。
在禁用重做日志记录时服务器意外停止后,尝试重新启动服务器被拒绝,错误如下:

[ERROR] [MY-013598] [InnoDB] Server was killed when Innodb Redo 
logging was disabled. Data files could be corrupt. You can try 
to restart the database with innodb_force_recovery=6

在这种情况下,初始化一个新的MySQL实例并再次启动数据加载过程。

需要INNODB_REDO_LOG_ENABLE权限来启用和禁用重做日志。

Innodb_redo_log_enabled 状态变量允许监视重做日志状态。

禁用重做日志时,不允许克隆操作和重做日志归档,反之亦然。

ALTER INSTANCE [ENABLE|DISABLE] INNODB REDO_LOG 操作需要一个排他的备份元数据锁,这个锁阻止其他ALTER INSTANCE操作并发执行。其他ALTER INSTANCE操作必须等待该锁释放才能执行。

下列步骤描述了当向新 MySQL 实例加载数据时如何禁用重做日志。

  1. 在新 MySQL 实例上,将INNODB_REDO_LOG_ENABLE权限授予负责禁用重做日志的用户账户。
mysql> GRANT INNODB_REDO_LOG_ENABLE ON *.* to 'data_load_admin';
  1. 使用 data_load_admin 用户,禁用重做日志:
mysql> ALTER INSTANCE DISABLE INNODB REDO_LOG;
  1. 检查 Innodb_redo_log_enabled 状态变量确保重做日志被禁用。
mysql> SHOW GLOBAL STATUS LIKE 'Innodb_redo_log_enabled';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Innodb_redo_log_enabled | OFF   |
+-------------------------+-------+
  1. 执行数据加载操作。
  2. 使用 data_load_admin 用户,在数据加载操作完成后启用重做日志:
mysql> ALTER INSTANCE ENABLE INNODB REDO_LOG;
  1. 检查 Innodb_redo_log_enabled 状态变量确保重做日志被启用:
mysql> SHOW GLOBAL STATUS LIKE 'Innodb_redo_log_enabled';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Innodb_redo_log_enabled | ON    |
+-------------------------+-------+

相关话题

  • 重做日志配置
  • 第8.5.4节 “优化InnoDB重做日志记录”
  • 重做日志加密

15.6.6 撤销日志(Undo Logs)

**撤消日志(又叫做还原日志,Undo Logs,或 Undo 日志)**是与单个读写事务关联的撤消日志记录的集合。**撤消日志记录(Undo Log Record)**包含有关如何撤消事务对聚集索引记录的最新更改的信息。如果另一个事务需要在一致性读取操作中查看原始数据,则会从撤消日志记录中检索未修改的数据。撤消日志存在于撤消日志段中,撤消日志段包含在回滚段中。回滚段位于撤消表空间和全局临时表空间中。

位于全局临时表空间的撤消日志用于修改用户定义的临时表中的数据的事务。这些撤消日志不是重做日志,因为崩溃恢复不需要这些日志。它们仅在服务器运行时用于回滚。这种类型的撤消日志可以避免重做日志I/O,从而提高性能。

有关撤消日志的 静态数据(data-at-rest) 加密的信息,请参见 撤消日志加密。

每个撤消表空间和全局临时表空间分别支持最多 128 个回滚段。innodb_rollback_segments 变量定义回滚段的数量。

回滚段支持的事务数取决于回滚段中的撤消槽数和每个事务所需的撤消日志数。回滚段中的撤消槽数根据 InnoDB 页面大小而不同。

InnoDB 页大小 回滚段中的Undo 插槽数 (InnoDB 页大小 / 16)
4096 (4KB) 256
8192 (8KB) 512
16384 (16KB) 1024
32768 (32KB) 2048
65536 (64KB) 4096

一个事务最多分配四个撤消日志,以下操作类型各一个:

  1. 用户自定义表上的INSERT操作
  2. 用户自定义表上的UPDATEDELETE操作
  3. 用户自定义临时表上的INSERT操作
  4. 用户自定义临时表上的UPDATEDELETE操作

根据需要分配撤消日志。例如,对常规表和临时表执行INSERTUPDATEDELETE操作的事务需要完全分配四个撤消日志。仅对常规表执行INSERT操作的事务需要一个撤消日志。

对常规表执行操作的事务将从指定的撤消表空间回滚段中分配撤消日志。对临时表执行操作的事务将从分配的全局临时表空间回滚段中分配撤消日志。

分配给事务的撤消日志在其持续时间内与事务保持连接。例如,为常规表上的INSERT操作分配给事务的撤消日志用于该事务对常规表执行的所有INSERT运算。

考虑到上述因素,可以使用以下公式来估计 InnoDB 能够支持的并发读写事务数。

  • 如果每个事务执行INSERTUPDATEDELETE操作,InnoDB 能够支持的并发读写事务数为:
(innodb_page_size / 16) * innodb_rollback_segments * number of undo tablespaces
  • 如果每个事务执行INSERTUPDATEDELETE操作,InnoDB 能够支持的并发读写事务数为:
(innodb_page_size / 16 / 2) * innodb_rollback_segments * number of undo tablespaces
  • 如果每个事务对临时表执行INSERT操作,InnoDB 能够支持的并发读写事务数为:
(innodb_page_size / 16) * innodb_rollback_segments
  • 如果每个事务对临时表执行INSERTUPDATEDELETE操作,InnoDB 能够支持的并发读写事务数为:
(innodb_page_size / 16 / 2) * innodb_rollback_segments

注意
在达到 InnoDB 能够支持的并发读写事务数之前,可能会遇到并发事务限制错误。当分配给事务的回滚段用完撤消槽时,就会发生这种情况。在这种情况下,请尝试重新运行事务。
当事务对临时表执行操作时,InnoDB 能够支持的并发读写事务的数量受分配给全局临时表空间的回滚段的数量限制,默认情况下为 128。

你可能感兴趣的:(#,第,15,章,InnoDB,存储引擎,《MySQL,8.0,参考手册》中文翻译,数据库,mysql,dba)