15.6.1.3-导入InnoDB表

可迁移表空间允许导入普通表,分区表,或独立表空间内的独立表分区。本章介绍怎样使用可迁移表空间特性来导入表。需要导入表功能的原因有可能有很多:

  • 在一个非生产mysql实例上运行报告,以避免给生产环境实例带来额外的负载。
  • 复制数据到一个新副本实例中。
  • 从一个备份表空间文件中恢复数据。
  • 作为一种比导入dump文件更快的迁移数据的方式,而后者需要重新插入数据并重建索引。
  • 将数据移动到到一个存储媒介更符合你的存储要求的实例上。例如,你可以将繁忙的表移动到一个SSD设备上,或将大表移动到一个大容量HDD设备上。

本章节会从以下几个方面介绍可迁移表空间功能:

先决条件 #

  • 必须启用innodb_file_per_table变量。这个是默认开启的。

  • 表空间的页大小必须和目标实例的页大小匹配。innodb_page_size定义了InnoDB页大小,这个变量在mysql实例初始化的时候被设置的。

  • 如果一张表有外键约束,则必须在执行discard tablespace操作之前禁用foreign_key_checks。此外,你应该在同一逻辑时间点导出所有外键相关的表,因为ALTER TABLE … IMPORT TABLESPACE不会在导入的数据上强制执行外键约束。为此,停止更新相关表,提交所有事务,获取表上的共享锁,然后执行导出操作。

  • 在从另一个MySQL实例上导入一张表时,两个MySQL实例必须有GA状态,必须有相同的版本。否则,这张表就只能在在同一个实例上导入。

  • 如果一张表时在外部目录创建(通过在CREATTE TABLE语句添加DATA DIRECTORY子句创建),那在副本上导入时,创建表时就必须有相同的DATA DIRECTORY子句。如果子句不匹配,就会报数据库比匹配错误(schema mismatch error)。要确定源表的DATA DIRECTORY子句,可以使用SHOW CREATE TABLE语句来查看表的定义。想了解更多关于 DATA DIRECTORY 子句的信息,请查阅15.6.1.2,“在外部创建表”

  • 如果在表定义中没有显式指定ROW_FORMACT或使用了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;
  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 UPDATE时,InnoDB在表的schema目录下生成一个 .cfg 元数据文件。.cfg文件包含了用来在导入操作中做校验的表定义。

执行 FLUSH TABLE … FOR EXPORT 的连接必须在操作进行过程中保持open状态。否则在连接关闭时, .cfg 文件会被移除,锁会被释放。
  1. 将 .ibd 文件和 .cfg 元数据文件从源实例复制到目标实例中,例如:
$> scp /path/to/datadir/test/t1.{ibd,cfg} destination-server:/path/to/datadir/test

必须在释放共享锁之前复制 .ibd 文件和 .cfg 文件,就像下一步描述的那样。

如果你从一个加密表空间导入表,InnoDB还会生成一个 .cfp 文件。.cfg 文件必须和 .cfg文件一起复制到目标实例。 .cfg文件包含了一个迁移key和加密表空间key。导入时,InnoDB使用迁移key来解密表空间key。想了解相关信息,请查阅15.13,“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. 在目标实例上,创建一个表定义和源实例相同的分区表。如果表定义不匹配,在你尝试导入操作时就会报数据库(或者是定义不匹配)不匹配错误。
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来关闭(quiesce)要导入的表。当一张表被关闭后,只允许在表上进行只读事务。
mysql> USE test;
mysql> FLUSH TABLES t1 FOR EXPORT;

FLUSH TABLES … FOR EXPORT可以确保相关表上的变更都被刷新到了磁盘上,这样就可以在实例运行时获取二级制副本。在运行 FLUSH TABLES … FOR EXPORT时,InnoDB在在表的schema目录下为这张表的每一个表空间文件都生成了一个 .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还会生成一个 .cfp 文件。.cfg 文件必须和 .cfg文件一起复制到目标实例。 .cfg文件包含了一个迁移key和加密表空间key。导入时,InnoDB使用迁移key来解密表空间key。想了解相关信息,请查阅15.13,“InnoDB数据静态加密”
  1. 在源实例上,使用 unlock tables 来释放 flush tables … for export获取的锁:
mysql> USE test;
mysql> UNLOCK TABLES;
  1. 在目标实例上,导入分区表的表空间:
mysql> USE test;
mysql> ALTER TABLE t1 IMPORT TABLESPACE;

导入表分区 #

这个例子展示了怎样导入一个单独的表分区,每一个表分区都位于一个独立表空间文件内。

下面的例子中,导入了一张分区表(有4个分区)的两个分区(p2 和 p3)。

  1. 在目标实例上,创建一个表定义和源实例相同的分区表。如果表定义不匹配,在你尝试导入操作时就会报数据库(或者是定义不匹配)不匹配错误。
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来关闭(quiesce)要导入的表。当一张表被关闭后,只允许在表上进行只读事务。
mysql> USE test;
mysql> FLUSH TABLES t1 FOR EXPORT;

FLUSH TABLES … FOR EXPORT 确保了相关表上的所有变更都被刷新到了磁盘上,这样就可以在实例运行时获取二进制副本。在运行 FLUSH TABLES … FOR EXPORT 时,InnoDB在在表的schema目录下为这张表的每一个表空间文件都生成了一个 .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还会生成一个 .cfp 文件。.cfg 文件必须和 .cfg文件一起复制到目标实例。 .cfg文件包含了一个迁移key和加密表空间key。导入时,InnoDB使用迁移key来解密表空间key。想了解相关信息,请查阅15.13,“InnoDB数据静态加密”
  1. 在源实例上,使用 unlock tables 来释放 flush tables … for export获取的锁:
mysql> USE test;
mysql> UNLOCK TABLES;
  1. 在目标实例上,导入表分区p2和p3:
mysql> USE test;
mysql> ALTER TABLE t1 IMPORT PARTITION p2, p3 TABLESPACE;
在子分区表上执行 ALTER TABLE … IMPORT PARTITION … TABLESPACE 时,使用分区名或子分区名都可以。当指定分区名时,这个操作会包含这个分区内的所有子分区。

限制 #

  • 只有位于独立表空间中的表才支持可移动表空间功能。系统表空间和通用表空间中的表则不支持。共享表空间中的表不能被关闭(quiesecd)。

  • 有全文索引的表不支持 FLUSH TABLES … FOR EXPORT 操作,因为不能刷新全文搜索辅助表。在导入一个有全文索引的表之后,执行 OPTIMIZE TABLEL 来重建全文索引。或者,在导出操作之前,删除全文索引,在目标实例上导入表之后再重建索引。

  • 因为 .cfg 元数据文件限制,导入分区表时,schema不匹配信息中不会包含分区类型和分区定义差异。会包含列差异。

  • 在MySQL8.0.19之前,.cfg元数据文件中不会存储索引键排序方向信息。默认认为索引键排序方向是升序的。因此,如果定义索引时使用了 desc,导入操作就可能会以非预期的方向排序记录。一个解决办法是删除然后重建相关索引。想了解更多关于索引键方向的信息,请查阅章节13.1.15,“CREATE INDEX 语句”

    在MySQL 8.0.19版本中,更新了 .cfg 文件的格式,包含了索引键排序方向信息。所以上面描述的信息,在MySQL8.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)

只有在没有schema不匹配且表不包含即时添加列和删除类时,应该考虑使用无 .cfg 元数据文件导入。无 .cfg 文件导入功能在崩溃恢复时很有用。

尝试在无 .cfg 文件的情况下导入一张有即时添加列或删除列的表(使用ALGORITHM=INSTANT)会导致未知行为。

  • 在Windows上,InnoDB在内部存储数据库,表空间,和表名时使用小小。要避免在大小写敏感的操作系统(比如linux和unix)上导入表时出现问题,创建所有数据库,表空间,和表时使用小写名称。确保所有名称都是小写的方法是在初始化数据库实例之前,将lower_case_table_names设置为1.(在实例被初始化之后,不允许在启动时修改lower_case_table_names)。

  • 在子分区表上执行 ALTER TABLE … DISCARD PARTITION … TABLESPACE 和 ALTER TABLE … IMPORT PARTITION … TABLESPACE,使用分区名和子分区名都可以。如果指定分区名,这个操作会包含这个分区内的所有子分区。

内部 #

下面介绍在导入表过程中的一些内部细节和写入错误日志的信息。

当在目标实例上执行 ALTER TABLE … DISCARD TABLESPACE 时:

  • 相关表被用 排它锁 锁定。
  • 相关表空间被从表定义中移除(detached)。

当在源实例上执行 FLUSH TABLES … FOR EXPORT 时:

  • 相关表被用 共享锁 锁定。
  • purge协调线程被停止。
  • 脏页被刷到磁盘上。
  • 表的元数据被写入到二进制的 .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 文件被删除。
  • 导入表上的共享锁被移除,purge协调线程被重启。

这个操作可能会产生如下错误日志信息:

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

当在目标实例上执行 ALTER TABLE … IMPORT TABLESPACE 时,导入算法对每一个导入的表空间都会执行如下操作:

  • 会检查每一个表空间页的完整性。
  • 会更新每一个页上的表空间ID和LSN。
  • header Page指给你的标志被校验,lsn倍更新。
  • B+树索引被更新。
  • 页状态被更新为dirty,以便被刷新到磁盘上。

这个操作会产生如下错误日志信息:

[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