官网地址:MySQL :: MySQL 5.7 Reference Manual :: 14.6.1.1 Creating InnoDB Tables
欢迎关注留言,我是收集整理小能手,工具翻译,仅供参考,笔芯笔芯.
InnoDB
使用该语句创建表 CREATE TABLE;例如:
CREATE TABLE t1 (a INT, b CHAR (20), PRIMARY KEY (a)) ENGINE=InnoDB;
当被定义为默认存储引擎(默认情况下)时,ENGINE=InnoDB
不需要 该子句。InnoDB
但是, 如果要在默认存储引擎不是或未知的不同 MySQL Server 实例上重播ENGINE
该语句,则该子句很有用 。您可以通过发出以下语句来确定 MySQL Server 实例上的默认存储引擎: CREATE TABLEInnoDB
mysql> SELECT @@default_storage_engine;
+--------------------------+
| @@default_storage_engine |
+--------------------------+
| InnoDB |
+--------------------------+
InnoDB
默认情况下,表是在每个表文件的表空间中创建的。InnoDB
要在系统表空间中创建表InnoDB
,请innodb_file_per_table 在创建表之前禁用该变量。要 InnoDB
在通用表空间中创建表,请使用 CREATE TABLE ... TABLESPACE语法。有关更多信息,请参见 第 14.6.3 节“表空间”。
MySQL 将表的数据字典信息存储在 数据库目录中的.frm 文件中。与其他 MySQL 存储引擎不同, InnoDB
它还在系统表空间内的自己的内部数据字典中对有关表的信息进行编码。当MySQL删除一张表或一个数据库时,它会删除一个或多个.frm
文件以及InnoDB
数据字典中的相应条目。您不能InnoDB
仅通过移动文件来在数据库之间移动表.frm
。有关移动InnoDB
表的信息,请参阅第 14.6.1.4 节 “移动或复制 InnoDB 表”。
表的行格式InnoDB
决定了其行在磁盘上的物理存储方式。 InnoDB
支持四种行格式,每种格式具有不同的存储特性。支持的行格式包括 REDUNDANT
、COMPACT
、 DYNAMIC
和COMPRESSED
。行DYNAMIC
格式是默认格式。有关行格式特征的信息,请参阅 第 14.11 节 “InnoDB 行格式”。
该innodb_default_row_format 变量定义默认的行格式。表的行格式也可以使用 or语句ROW_FORMAT
中的 table 选项显式定义。请参阅 定义表的行格式。 CREATE TABLE
ALTER TABLE
建议您为创建的每个表定义一个主键。选择主键列时,选择具有以下特征的列:
最重要的查询引用的列。
永远不会留空的列。
永远不会有重复值的列。
一旦插入,列的值就很少改变。
例如,在包含人员信息的表中,您不会创建主键,(firstname, lastname)
因为多个人可能具有相同的姓名,姓名列可能留空,有时人们会更改姓名。由于存在如此多的约束,通常没有一组明显的列可以用作主键,因此您需要创建一个带有数字 ID 的新列来充当主键的全部或部分。您可以声明 自动递增列,以便在插入行时自动填充升序值:
# 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));
有关自动增量列的更多信息,请参见 第 14.6.1.6 节 “InnoDB 中的 AUTO_INCRMENT 处理”。
尽管表无需定义主键即可正常工作,但主键涉及性能的许多方面,并且对于任何大型或频繁使用的表来说都是至关重要的设计方面。建议您始终在CREATE TABLE语句中指定主键。如果创建表,加载数据,然后运行 ALTER TABLE添加主键,那么该操作比创建表时定义主键要慢得多。有关主键的更多信息,请参阅第 14.6.2.1 节 “聚集索引和辅助索引”。
要查看表的属性InnoDB
,请发出以下SHOW TABLE STATUS 语句:
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输出的信息,请参见 第 13.7.5.36 节 “SHOW TABLE STATUS 语句”。
您还可以InnoDB
通过查询信息模式系统表来访问表属性InnoDB
:
mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME='test/t1' \G
*************************** 1. row ***************************
TABLE_ID: 45
NAME: test/t1
FLAG: 1
N_COLS: 5
SPACE: 35
FILE_FORMAT: Barracuda
ROW_FORMAT: Dynamic
ZIP_PAGE_SIZE: 0
SPACE_TYPE: Single
有关更多信息,请参见 第 14.16.3 节,“InnoDB INFORMATION_SCHEMA 系统表”。
在外部创建表有不同的原因InnoDB
;也就是说,在数据目录之外创建表。例如,这些原因可能包括空间管理、I/O 优化或将表放置在具有特定性能或容量特征的存储设备上。
InnoDB
支持以下外部建表方式:
您可以通过在语句 中InnoDB
指定子句来在外部目录中创建表。 DATA DIRECTORY
CREATE TABLE
CREATE TABLE t1 (c1 INT PRIMARY KEY) DATA DIRECTORY = '/external/directory';
DATA DIRECTORY
在每表文件表空间中创建的表支持 该子句。innodb_file_per_table当启用该变量时(默认情况下), 表会在 file-per-table 表空间中隐式创建 。
mysql> SELECT @@innodb_file_per_table;
+-------------------------+
| @@innodb_file_per_table |
+-------------------------+
| 1 |
+-------------------------+
有关每表文件表空间的更多信息,请参见 第 14.6.3.2 节 “每表文件表空间”。
请确保您选择的目录位置,因为 以后DATA DIRECTORY
不能使用该子句来 ALTER TABLE更改位置。
DATA DIRECTORY
当您在 语句中 指定子句时,将在指定目录下的 schema 目录中创建表CREATE TABLE
的数据文件 ( ),并在 MySQL 数据目录下的 schema 目录中创建包含数据文件路径的文件 ( )。文件 的功能与符号链接类似。(不支持将实际符号链接与数据文件一起使用 。) table_name
.ibd.isl
table_name
.isl.isl
InnoDB
以下示例演示使用 子句在外部目录中创建表DATA DIRECTORY
。假设该 innodb_file_per_table变量已启用。
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
# An .isl file that contains the data file path is created
# in the schema directory under the MySQL data directory
$> cd /path/to/mysql/data/test
$> ls
db.opt t1.frm t1.isl
使用注意事项:
MySQL 最初将表空间数据文件保持打开状态,防止您卸载设备,但如果服务器繁忙,最终可能会关闭该文件。请小心不要在 MySQL 运行时意外卸载外部设备,或在设备断开连接时启动 MySQL。当关联的数据文件丢失时尝试访问表会导致严重错误,需要重新启动服务器。
如果在预期路径中找不到数据文件,服务器重新启动可能会失败。在这种情况下,请手动
.isl
从架构目录中删除该文件。重启后,drop表即可 从数据字典.frm
中删除该文件和该表的信息。在将表放置在 NFS 安装的卷上之前,请查看将 NFS 与 MySQL 配合使用中概述的潜在问题。
如果使用 LVM 快照、文件复制或其他基于文件的机制来备份表的数据文件,请始终首先使用该 FLUSH TABLES ... FOR EXPORT语句以确保 在备份发生之前 将内存中缓冲的所有更改刷新到磁盘。
使用
DATA DIRECTORY
子句在外部目录中创建表是使用 符号链接的替代方法,但 符号链接InnoDB
不支持。DATA DIRECTORY
在源和副本驻留在同一主机上的复制环境中,不支持 该子句。该DATA DIRECTORY
子句需要完整的目录路径。在这种情况下复制路径将导致源和副本在同一位置创建表。
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';
此方法仅支持在 file-per-table 表空间中创建的表,但不需要 innodb_file_per_table启用该变量。CREATE TABLE ... DATA DIRECTORY
在所有其他方面,该方法与上述方法等效。相同的使用说明也适用。
您可以在驻留在外部目录中的通用表空间中创建表。
有关在外部目录中创建通用表空间的信息,请参阅 创建通用表空间。
有关在通用表空间中创建表的信息,请参阅 将表添加到通用表空间。
本节介绍如何使用 可传输表空间功能导入表,该功能允许导入驻留在每个表文件表空间中的表、分区表或单个表分区。您可能想要导入表的原因有很多:
在非生产 MySQL 服务器实例上运行报告,以避免对生产服务器造成额外负载。
将数据复制到新的副本服务器。
从备份的表空间文件恢复表。
这是比导入转储文件更快的数据移动方式,后者需要重新插入数据并重建索引。
将数据移动到具有更适合您的存储要求的存储介质的服务器。例如,您可以将繁忙的表移动到 SSD 设备,或将大型表移动到大容量 HDD 设备。
本节的以下主题描述了 可传输表空间 功能:
该innodb_file_per_table 变量必须启用,默认情况下是启用的。
表空间的页面大小必须与目标 MySQL 服务器实例的页面大小匹配。
InnoDB
页大小由变量定义 innodb_page_size,该变量在初始化 MySQL 服务器实例时配置。如果表有外键关系, foreign_key_checks执行前必须禁用
DISCARD TABLESPACE
。此外,您应该在同一逻辑时间点导出所有外键相关表,因为 ALTER TABLE ... IMPORT TABLESPACE不会对导入的数据强制外键约束。为此,请停止更新相关表,提交所有事务,获取表上的共享锁,然后执行导出操作。从另一个 MySQL 服务器实例导入表时,两个 MySQL 服务器实例必须具有通用可用性 (GA) 状态,并且版本必须相同。否则,该表必须在导入该表的同一 MySQL 服务器实例上创建。
DATA DIRECTORY
如果通过在语句中指定子句 在外部目录中创建表CREATE TABLE,则您在目标实例上替换的表必须使用相同的DATA DIRECTORY
子句进行定义。如果子句不匹配,则会报告架构不匹配错误。要确定源表是否是使用DATA DIRECTORY
子句定义的,请使用 SHOW CREATE TABLE查看表定义。有关使用该DATA DIRECTORY
子句的信息,请参阅 第 14.6.1.2 节 “从外部创建表”。如果
ROW_FORMAT
在表定义中未显式定义或ROW_FORMAT=DEFAULT
使用某个选项,则 innodb_default_row_format 源实例和目标实例上的设置必须相同。否则,当您尝试导入操作时,会报告架构不匹配错误。用于 SHOW CREATE TABLE检查表定义。用于SHOW VARIABLES检查 innodb_default_row_format 设置。有关相关信息,请参阅 定义表的行格式。
此示例演示如何导入驻留在每表文件表空间中的常规非分区表。
在目标实例上,创建一个与要导入的表具有相同定义的表。(您可以使用语法获取表定义SHOW CREATE TABLE。)如果表定义不匹配,则尝试导入操作时会报告架构不匹配错误。
mysql> USE test; mysql> CREATE TABLE t1 (c1 INT) ENGINE=INNODB;
在目标实例上,丢弃您刚刚创建的表的表空间。(导入前必须丢弃接收表的表空间。)
mysql> ALTER TABLE t1 DISCARD TABLESPACE;
在源实例上,运行 FLUSH TABLES ... FOR EXPORT以停顿您要导入的表。当表停顿时,该表上只允许进行只读事务。
mysql> USE test; mysql> FLUSH TABLES t1 FOR EXPORT;
FLUSH TABLES ... FOR EXPORT确保对指定表的更改刷新到磁盘,以便在服务器运行时可以创建二进制表副本。FLUSH TABLES ... FOR EXPORT运行 时 ,会在表的模式目录中
InnoDB
生成 元数据文件。.cfg
该.cfg
文件包含用于导入操作期间架构验证的元数据。笔记FLUSH TABLES ... FOR EXPORT操作运行时, 执行的连接 必须保持打开状态;否则,
.cfg
当连接关闭时释放锁时,该文件将被删除。.ibd
将文件和 元数据文件从源实例 复制.cfg
到目标实例。例如:$> scp /path/to/datadir/test/t1.{ibd,cfg} destination-server:/path/to/datadir/test
在释放共享锁之前必须复制文件
.ibd
和.cfg
文件,如下一步所述。笔记如果要从加密表空间导入表, 除了 元数据文件之外,
InnoDB
还会生成一个 文件。该 文件必须与该文件一起复制到目标实例 。该 文件包含传输密钥和加密的表空间密钥。导入时, 使用传输密钥来解密表空间密钥。有关相关信息,请参阅 第 14.14 节 “InnoDB 静态数据加密”。.cfp
.cfg
.cfp
.cfg
.cfp
InnoDB
在源实例上,使用 UNLOCK TABLES释放语句获取的锁 FLUSH TABLES ... FOR EXPORT:
mysql> USE test; mysql> UNLOCK TABLES;
该UNLOCK TABLES操作还会删除该
.cfg
文件。在目标实例上,导入表空间:
mysql> USE test; mysql> ALTER TABLE t1 IMPORT TABLESPACE;
此示例演示如何导入分区表,其中每个表分区都驻留在每个表文件的表空间中。
在目标实例上,创建一个与要导入的分区表具有相同定义的分区表。(您可以使用语法获取表定义 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/ db.opt t1.frm t1#P#p0.ibd t1#P#p1.ibd t1#P#p2.ibd
在目标实例上,丢弃分区表的表空间。(在导入操作之前,必须丢弃接收表的表空间。)
mysql> ALTER TABLE t1 DISCARD TABLESPACE;
分区表的三个表空间
.ibd
文件从目录中丢弃 ,留下以下文件:/
datadir
/testmysql> \! ls /path/to/datadir/test/ db.opt t1.frm
在源实例上,运行 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/ db.opt t1#P#p0.ibd t1#P#p1.ibd t1#P#p2.ibd t1.frm t1#P#p0.cfg t1#P#p1.cfg t1#P#p2.cfg
这些
.cfg
文件包含导入表空间时用于架构验证的元数据。 FLUSH TABLES ... FOR EXPORT只能在表上运行,不能在单个表分区上运行。.ibd
将和.cfg
文件从源实例架构目录 复制到目标实例架构目录。例如:$>scp /path/to/datadir/test/t1*.{ibd,cfg} destination-server:/path/to/datadir/test
.ibd
在释放共享锁之前必须复制和 文件.cfg
,如下一步所述。笔记如果要从加密表空间导入表, 除了 元数据文件之外,
InnoDB
还会生成一个 文件。文件 必须与文件一起复制到目标实例 。这些 文件包含传输密钥和加密的表空间密钥。导入时, 使用传输密钥来解密表空间密钥。有关相关信息,请参阅 第 14.14 节 “InnoDB 静态数据加密”。.cfp
.cfg
.cfp
.cfg
.cfp
InnoDB
在源实例上,使用 UNLOCK TABLES释放通过以下方式获取的锁 FLUSH TABLES ... FOR EXPORT:
mysql> USE test; mysql> UNLOCK TABLES;
在目标实例上,导入分区表的表空间:
mysql> USE test; mysql> ALTER TABLE t1 IMPORT TABLESPACE;
此示例演示如何导入各个表分区,其中每个分区驻留在每个表的文件表空间文件中。
在以下示例中,导入四分区表的 两个分区(p2
和)。p3
在目标实例上,创建一个与要从中导入分区的分区表具有相同定义的分区表。(您可以使用语法获取表定义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/ db.opt t1.frm t1#P#p0.ibd t1#P#p1.ibd t1#P#p2.ibd t1#P#p3.ibd
在目标实例上,丢弃要从源实例导入的分区。(在导入分区之前,必须从接收分区表中丢弃相应的分区。)
mysql> ALTER TABLE t1 DISCARD PARTITION p2, p3 TABLESPACE;
.ibd
两个废弃分区的 表空间文件将从 目标实例上的目录中删除,留下以下文件:/
datadir
/testmysql> \! ls /path/to/datadir/test/ db.opt t1.frm t1#P#p0.ibd t1#P#p1.ibd
笔记当ALTER TABLE ... DISCARD PARTITION ... TABLESPACE在子分区表上运行时,分区和子分区表名称都是允许的。当指定分区名称时,该分区的子分区将包含在操作中。
在源实例上,运行 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/ db.opt t1#P#p0.ibd t1#P#p1.ibd t1#P#p2.ibd t1#P#p3.ibd t1.frm t1#P#p0.cfg t1#P#p1.cfg t1#P#p2.cfg t1#P#p3.cfg
这些
.cfg
文件包含在导入操作期间用于架构验证的元数据。 FLUSH TABLES ... FOR EXPORT只能在表上运行,不能在单个表分区上运行。将分区 和分区的
.ibd
和 文件 从源实例架构目录 复制到目标实例架构目录。.cfg
p2
p3
$> 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
还会生成一个 文件。文件 必须与文件一起复制到目标实例 。这些 文件包含传输密钥和加密的表空间密钥。导入时, 使用传输密钥来解密表空间密钥。有关相关信息,请参阅 第 14.14 节 “InnoDB 静态数据加密”。.cfp
.cfg
.cfp
.cfg
.cfp
InnoDB
在源实例上,使用 UNLOCK TABLES释放通过以下方式获取的锁 FLUSH TABLES ... FOR EXPORT:
mysql> USE test; mysql> UNLOCK TABLES;
在目标实例上,导入表分区
p2
并p3
:mysql> USE test; mysql> ALTER TABLE t1 IMPORT PARTITION p2, p3 TABLESPACE;
笔记当ALTER TABLE ... IMPORT PARTITION ... TABLESPACE在子分区表上运行时,分区和子分区表名称都是允许的。当指定分区名称时,该分区的子分区将包含在操作中。
可传输表空间 功能仅支持驻留在每表文件表空间中的表。驻留在系统表空间或通用表空间中的表不支持。共享表空间中的表无法停顿。
FLUSH TABLES ... FOR EXPORT具有索引的表不支持
FULLTEXT
,因为无法刷新全文搜索辅助表。导入带有索引的表后FULLTEXT
,运行 OPTIMIZE TABLE重建FULLTEXT
索引。或者,FULLTEXT
在导出操作之前删除索引,并在目标实例上导入表后重新创建索引。由于
.cfg
元数据文件限制,导入分区表时不会报告分区类型或分区定义差异的架构不匹配情况。报告列差异。
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
仅当预计不会出现架构不匹配时,才应考虑 导入不带元数据文件的表。.cfg
在无法访问元数据的崩溃恢复场景中, 无需文件即可导入的功能 可能非常有用。在 Windows 上,
InnoDB
在内部以小写形式存储数据库、表空间和表名称。为了避免在区分大小写的操作系统(例如 Linux 和 Unix)上出现导入问题,请使用小写名称创建所有数据库、表空间和表。实现此目的的一种便捷方法是 在创建数据库、表空间或表之前 添加lower_case_table_names=1到您的或文件[mysqld]
的部分 :my.cnf
my.ini
[mysqld] lower_case_table_names=1
ALTER TABLE ... DISCARD PARTITION ... TABLESPACE在子分区表上 运行时 ALTER 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 和日志序列号 (LSN) 都会更新。
验证标志并更新标头页的 LSN。
Btree 页面已更新。
页面状态设置为脏,以便将其写入磁盘。
此操作的预期错误日志消息:
[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/5.7/en/innodb-troubleshooting.html
InnoDB
本节介绍将部分或全部表移动或复制到不同服务器或实例的 技术 。例如,您可以将整个 MySQL 实例迁移到更大、更快的服务器;您可以将整个 MySQL 实例克隆到新的副本服务器;您可以将各个表复制到另一个实例以开发和测试应用程序,或者复制到数据仓库服务器以生成报告。
在 Windows 上,InnoDB
始终以小写形式在内部存储数据库和表名称。要将二进制格式的数据库从 Unix 移动到 Windows 或从 Windows 移动到 Unix,请使用小写名称创建所有数据库和表。完成此操作的一种便捷方法是在创建任何数据库或表之前 将以下行添加到 您的或文件[mysqld]
的部分 :my.cnf
my.ini
[mysqld]
lower_case_table_names=1
移动或复制InnoDB
表的技术包括:
导入表格
驻留在每表文件表空间中的表可以从另一个 MySQL 服务器实例或使用可 传输表空间功能的备份导入。请参见 第 14.6.1.3 节,“导入 InnoDB 表”。
MySQL 企业备份
MySQL Enterprise Backup 产品可让您备份正在运行的 MySQL 数据库,同时将操作中断降至最低,同时生成一致的数据库快照。当 MySQL Enterprise Backup 复制表时,读取和写入可以继续。此外,MySQL Enterprise Backup 可以创建压缩备份文件,并备份表的子集。与MySQL二进制日志结合,可以执行时间点恢复。MySQL Enterprise Backup 包含在 MySQL Enterprise 订阅中。
有关 MySQL Enterprise Backup 的更多详细信息,请参阅 第 28.2 节 “MySQL Enterprise Backup 概述”。
复制数据文件(冷备份方式)
您只需复制第 14.19.1 节“InnoDB 备份”InnoDB
中“冷备份”下列出的所有相关文件即可移动数据库 。
InnoDB
数据和日志文件在具有相同浮点数格式的所有平台上都是二进制兼容的。如果浮点格式不同但您没有在表中使用 FLOAT或 DOUBLE数据类型,则过程是相同的:只需复制相关文件即可。
当您移动或复制 file-per-table.ibd
文件时,源系统和目标系统上的数据库目录名称必须相同。存储在共享表空间中的表定义 InnoDB
包括数据库名称。存储在表空间文件中的事务 ID 和日志序列号在数据库之间也有所不同。
要将.ibd
文件和关联表从一个数据库移动到另一个数据库,请使用以下RENAME TABLE语句:
RENAME TABLE db1.tbl_name TO db2.tbl_name;
如果您有文件的 “干净”.ibd
备份,您可以将其恢复到其来源的 MySQL 安装,如下所示:
自复制文件以来,该表不得被删除或截断
.ibd
,因为这样做会更改存储在表空间内的表 ID。发出此ALTER TABLE语句来删除当前
.ibd
文件:ALTER TABLE tbl_name DISCARD TABLESPACE;
将备份
.ibd
文件复制到正确的数据库目录。发出此ALTER TABLE语句以告知对表
InnoDB
使用新 文件:.ibd
ALTER TABLE tbl_name IMPORT TABLESPACE;
笔记该ALTER TABLE ... IMPORT TABLESPACE功能不会对导入的数据强制执行外键约束。
在这种情况下,“干净”的 .ibd
文件备份是满足以下要求的备份:
文件中没有事务未提交的修改
.ibd
。文件中没有未合并的插入缓冲区条目
.ibd
。清除已从文件中删除所有带有删除标记的索引记录
.ibd
。mysqld已将
.ibd
文件的所有修改页从缓冲池刷新到文件。
您可以.ibd
使用以下方法制作干净的备份文件:
停止mysqld服务器 的所有活动并提交所有事务。
等到SHOW ENGINE INNODB STATUS显示数据库中没有活动事务,并且主线程状态
InnoDB
为Waiting for server activity
。然后您可以复制该.ibd
文件。
制作文件的干净副本的另一种方法 .ibd
是使用 MySQL Enterprise Backup 产品:
使用 MySQL Enterprise Backup 来备份安装
InnoDB
。在备份上启动第二个mysqld服务器并让它清理
.ibd
备份中的文件。
从逻辑备份恢复
您可以使用mysqldump 等实用程序来执行逻辑备份,该备份会生成一组 SQL 语句,执行这些语句可以重现原始数据库对象定义和表数据,以便传输到另一个 SQL 服务器。使用此方法,格式是否不同或者表是否包含浮点数据并不重要。
要提高此方法的性能,请 autocommit在导入数据时禁用。仅在导入整个表或表的段后执行提交。
如果您MyISAM想要转换表以InnoDB获得更好的可靠性和可扩展性,请在转换之前查看以下准则和提示。
当您从表过渡时MyISAM
,请降低配置选项的值 key_buffer_size以释放缓存结果不再需要的内存。增加配置选项的值 innodb_buffer_pool_size ,该选项执行与为表分配缓存内存类似的作用InnoDB
。缓冲 InnoDB
池缓存表数据和索引数据,加快查询查找速度,并将查询结果保留在内存中以供重用。有关缓冲池大小配置的指导,请参阅 第 8.12.4.1 节“MySQL 如何使用内存”。
在繁忙的服务器上,在关闭查询缓存的情况下运行基准测试。缓冲InnoDB
池提供了类似的好处,因此查询缓存可能会不必要地占用内存。有关查询缓存的信息,请参阅 第 8.10.3 节“MySQL 查询缓存”。
因为MyISAM
表不支持 事务,所以您可能没有太注意 autocommit配置选项和COMMITand ROLLBACK 语句。这些关键字对于允许多个会话同时读写InnoDB
表非常重要,从而在写入繁重的工作负载中提供显着的可扩展性优势。
当事务打开时,系统会保留事务开始时看到的数据快照,如果系统在杂散事务继续运行时插入、更新和删除数百万行,则可能会导致大量开销。因此,请注意避免事务运行时间过长:
如果您使用mysql会话进行交互式实验,请在完成时始终 COMMIT(完成更改)或 ROLLBACK(撤消更改)。关闭交互式会话而不是让它们长时间打开,以避免意外地使事务长时间打开。
ROLLBACK是一个相对昂贵的操作,因为 INSERT、 UPDATE、 和 操作在 之前 DELETE写入表,期望大多数更改都成功提交并且回滚很少。当试验大量数据时,避免对大量行进行更改,然后回滚这些更改。
InnoDB
COMMIT当使用一系列 INSERT语句加载大量数据时,请定期 COMMIT获取结果,以避免事务持续数小时。在数据仓库的典型加载操作中,如果出现问题,您将截断表(使用TRUNCATE TABLE)并从头开始,而不是执行 ROLLBACK.
前面的技巧可以节省在过长的事务期间可能浪费的内存和磁盘空间。当事务比应有的短时,问题就是 I/O 过多。对于每个 COMMIT,MySQL 确保每个更改都安全地记录到磁盘,这涉及一些 I/O。
对于表上的大多数操作
InnoDB
,您应该使用设置 autocommit=0。从效率的角度来看,当您发出大量连续的 INSERT、 UPDATE或 DELETE语句时,这可以避免不必要的 I/O。从安全角度来看, 如果您在mysql命令行或应用程序中的异常处理程序中 ROLLBACK 犯了错误,这允许您发出语句来恢复丢失或乱码的数据。autocommit=1适用于
InnoDB
运行一系列查询以生成报告或分析统计信息时的表。在这种情况下,不存在与 COMMIT或 相关的 I/O 损失ROLLBACK,并且InnoDB
可以 自动优化只读工作负载。如果您进行了一系列相关的更改,请一次性完成所有更改,并 COMMIT在最后进行一次更改。例如,如果您将相关信息插入多个表中,请在进行COMMIT 所有更改后执行一个操作。或者,如果您运行许多连续的 语句,请在加载所有数据后INSERT执行一个 ;COMMIT如果您正在执行数百万条 INSERT语句,也许可以通过每万或十万条记录发出一条来分割巨大的事务 COMMIT,这样事务就不会变得太大。
请记住,即使是一条SELECT 语句也会打开一个事务,因此在交互式mysql 会话中运行一些报告或调试查询后,要么发出 mysql 会话COMMIT ,要么关闭mysql会话。
有关相关信息,请参阅 第 14.7.2.2 节 “自动提交、提交和回滚”。
您可能 会 在 MySQL错误日志或 SHOW ENGINE INNODB STATUS. 对于表来说,死锁并不是 一个严重的问题InnoDB
,并且通常不需要任何纠正措施。当两个事务开始修改多个表并以不同的顺序访问表时,它们可能会达到每个事务都在等待另一个事务且两者都无法继续的状态。当 启用死锁检测 (默认)时,MySQL立即检测到这种情况并取消(回滚) “较小”交易,允许对方继续进行。如果使用配置选项禁用死锁检测 innodb_deadlock_detect ,则在发生死锁时InnoDB
将依赖于 innodb_lock_wait_timeout回滚事务的设置。
无论哪种方式,您的应用程序都需要错误处理逻辑来重新启动由于死锁而被强制取消的事务。当您重新发出与以前相同的 SQL 语句时,原来的计时问题不再适用。要么另一笔交易已经完成,您的交易可以继续,或者另一笔交易仍在进行中,您的交易需要等待直到完成。
如果死锁警告不断发生,您可以检查应用程序代码,以一致的方式重新排序 SQL 操作,或缩短事务。您可以在 innodb_print_all_deadlocks启用该选项的情况下进行测试,以查看 MySQL 错误日志中的所有死锁警告,而不仅仅是输出中的最后一个警告 SHOW ENGINE INNODB STATUS。
有关更多信息,请参见第 14.7.5 节“InnoDB 中的死锁”。
为了从表中获得最佳性能InnoDB
,您可以调整许多与存储布局相关的参数。
当 您转换MyISAM
大型、频繁访问并保存重要数据的表时,请调查并考虑语句的innodb_file_per_table、 innodb_file_format、 和 innodb_page_size变量以及 ROW_FORMAT 和KEY_BLOCK_SIZE子句CREATE TABLE。
在最初的实验中,最重要的设置是 innodb_file_per_table。启用此设置(从 MySQL 5.6.6 开始是默认设置)时, 会在file-per-tableInnoDB
表空间中隐式创建新表 。与系统表空间相比,每表文件表空间允许操作系统在表被截断或删除时回收磁盘空间。每表文件表空间还支持 Barracuda文件格式和相关功能,例如表压缩、长可变长度列的高效页外存储以及大索引前缀。有关更多信息,请参见 第 14.6.3.2 节 “每表文件表空间”InnoDB
。
您还可以将InnoDB
表存储在共享通用表空间中。通用表空间支持Barracuda文件格式,并且可以包含多个表。有关更多信息,请参见 第 14.6.3.3 节 “常规表空间”。
要将非InnoDB
表转换为使用,请 InnoDB
使用ALTER TABLE:
ALTER TABLE table_name ENGINE=InnoDB;
不要将mysql
数据库中的 MySQL 系统表转换 MyISAM
为InnoDB
表。这是不受支持的操作。如果这样做,MySQL不会重新启动,直到您从备份恢复旧系统表或通过重新初始化数据目录重新生成它们(请参见第 2.9.1节“初始化数据目录”)。
您可以创建一个InnoDB
MyISAM 表的克隆表,而不是用来ALTER TABLE执行转换,以便在切换之前并排测试新旧表。
创建一个InnoDB
具有相同列和索引定义的空表。使用查看 要使用的完整语句。将子句更改为 . SHOW CREATE TABLE
CREATE TABLEtable_name
\GENGINE
ENGINE=INNODB
要将大量数据传输到如上一 InnoDB
节所示创建的空表中,请插入带有. INSERT INTO
innodb_table
SELECT * FROM myisam_table
ORDER BY primary_key_columns
InnoDB
您还可以在插入数据后 为表创建索引。从历史上看,创建新的二级索引对于 来说是一个缓慢的操作 InnoDB
,但现在您可以在加载数据后创建索引,而索引创建步骤的开销相对较小。
如果对辅助键有UNIQUE
限制,则可以通过在导入操作期间暂时关闭唯一性检查来加快表导入速度:
SET unique_checks=0;
... import operation ...
SET unique_checks=1;
对于大表,这可以节省磁盘 I/O,因为 InnoDB
可以使用其 更改缓冲区批量写入二级索引记录。确保数据不包含重复的键。 unique_checks允许但不要求存储引擎忽略重复的键。
为了更好地控制插入过程,您可以分段插入大表:
INSERT INTO newtable SELECT * FROM oldtable
WHERE yourkey > something AND yourkey <= somethingelse;
插入所有记录后,您可以重命名表。
在大表转换过程中,增加 InnoDB
缓冲池的大小以减少磁盘I/O。通常,建议的缓冲池大小为系统内存的 50% 到 75%。InnoDB
您还可以增加日志文件 的大小 。
如果您打算 InnoDB
在转换过程中对表中的数据进行多个临时副本,建议您在每个表文件的表空间中创建表,以便在删除表时可以回收磁盘空间。当 innodb_file_per_table 启用配置选项(默认)时,新创建的 InnoDB
表将在每表文件表空间中隐式创建。
无论是直接转换MyISAM
表还是创建克隆InnoDB
表,请确保在此过程中有足够的磁盘空间来容纳旧表和新表。 InnoDB
表比表需要更多的磁盘空间MyISAM
。 如果某个ALTER TABLE操作用完空间,它就会开始回滚,如果是磁盘绑定的,则可能需要几个小时。对于插入,InnoDB
使用插入缓冲区将二级索引记录批量合并到索引中。这节省了大量的磁盘 I/O。对于回滚,没有使用这样的机制,并且回滚可能比插入花费更长的时间 30 倍。
在失控回滚的情况下,如果数据库中没有有价值的数据,建议终止数据库进程,而不是等待数百万个磁盘 I/O 操作完成。有关完整过程,请参见 第 14.22.2 节 “强制 InnoDB 恢复”。
该PRIMARY KEY
子句是影响MySQL查询性能以及表和索引空间使用的关键因素。主键唯一标识表中的行。表中的每一行都应该有一个主键值,并且任何两行都不能具有相同的主键值。
这些是主键的指南,后面是更详细的解释。
PRIMARY KEY
为每个表 声明一个。WHERE
通常,它是查找单行时 在子句中引用的最重要的列。PRIMARY KEY
在原始语句中 声明该子句CREATE TABLE ,而不是稍后通过 ALTER TABLE语句添加它。仔细选择列及其数据类型。优先选择数字列而不是字符或字符串列。
如果没有其他稳定的、唯一的、非空的数字列可供使用,请考虑使用自动增量列。
如果怀疑主键列的值是否会更改,那么自动增量列也是一个不错的选择。更改主键列的值是一项昂贵的操作,可能涉及重新排列表内和每个二级索引内的数据。
考虑向任何尚无主键的表添加主键。根据表的最大预计大小使用最小的实用数字类型。这可以使每行稍微更加紧凑,从而可以为大型表节省大量空间。如果表有任何 二级索引,则空间节省会成倍增加,因为主键值在每个二级索引条目中都会重复。除了减少磁盘上的数据大小之外,小的主键还可以让更多数据放入 缓冲池,从而加快各种操作并提高并发性。
如果表的某个较长列(例如 a )上已有主键,VARCHAR
请考虑添加新的无符号 AUTO_INCREMENT
列并将主键切换到该列,即使查询中未引用该列。此设计更改可以在二级索引中节省大量空间。您可以指定以前的主键列来UNIQUE NOT NULL
强制执行与子句相同的约束PRIMARY KEY
,即防止所有这些列出现重复值或空值。
如果将相关信息分散到多个表中,通常每个表都使用同一列作为其主键。例如,人事数据库可能有多个表,每个表都有一个员工编号的主键。销售数据库可能有一些表的主键为客户编号,而其他表的主键为订单号。由于使用主键的查找速度非常快,因此您可以为此类表构建高效的联接查询。
如果您PRIMARY KEY
完全省略该子句,MySQL 会为您创建一个不可见的子句。它是一个 6 字节的值,可能比您需要的长,从而浪费空间。由于它是隐藏的,因此您无法在查询中引用它。
与同等表相比,可靠性和可扩展性功能 InnoDB
需要更多的磁盘存储MyISAM
。您可以稍微更改列和索引定义,以获得更好的空间利用率、减少处理结果集时的 I/O 和内存消耗,以及更好的查询优化计划,从而有效地利用索引查找。
如果为主键设置数字 ID 列,请使用该值与任何其他表中的相关值交叉引用,特别是对于联接查询。例如,不要接受国家/地区名称作为输入并执行搜索相同名称的查询,而是执行一次查找以确定国家/地区 ID,然后执行其他查询(或单个联接查询)以在多个表中查找相关信息。不要将客户或目录商品编号存储为数字字符串(可能会占用多个字节),而是将其转换为数字 ID 以便存储和查询。4 字节无符号 INT该列可以索引超过 40 亿个项目(美国的十亿含义:10 亿)。有关不同整数类型的范围,请参见 第 11.1.2 节“整数类型(精确值)-INTEGER、INT、SMALLINT、TINYINT、MEDIUMINT、BIGINT”。
InnoDB
提供可配置的锁定机制,可以显着提高向包含列的表添加行的 SQL 语句的可伸缩性和性能 AUTO_INCREMENT
。要在表 中使用 AUTO_INCREMENT
该机制 ,必须将列定义为某个索引的第一列或唯一列,以便可以对表执行等效的索引查找以获得最大列值。索引不需要是 a或 ,但为了避免 列中出现重复值,建议使用这些索引类型。 InnoDB
AUTO_INCREMENT
SELECT MAX(
ai_col
)PRIMARY KEY
UNIQUE
AUTO_INCREMENT
本节介绍AUTO_INCREMENT
锁定模式、不同 AUTO_INCREMENT
锁定模式设置的使用含义以及如何 InnoDB
初始化 AUTO_INCREMENT
计数器。
本节介绍AUTO_INCREMENT
用于生成自动增量值的锁定模式,以及每种锁定模式如何影响复制。自增锁定模式是在启动时使用 innodb_autoinc_lock_mode 变量配置的。
以下术语用于描述 innodb_autoinc_lock_mode 设置:
“INSERT类似”的 陈述
在表中生成新行的所有语句,包括 INSERT、 INSERT ... SELECT、REPLACE、 REPLACE ... SELECT和LOAD DATA。包括“简单插入”、 “批量插入”和“混合模式” 插入。
“简单的插入”
可以预先确定要插入的行数的语句(在最初处理语句时)。这包括单行和多行 INSERT以及 REPLACE没有嵌套子查询的语句,但不包括 INSERT ... ON DUPLICATE KEY UPDATE.
“批量插入”
预先未知要插入的行数(以及所需的自动增量值的数量)的语句。这包括 INSERT ... SELECT、 REPLACE ... SELECT和LOAD DATA语句,但不包括普通的
INSERT
。在处理每一行时一次InnoDB
为该列分配一个新值。AUTO_INCREMENT
“混合模式插入”
这些是“简单插入”语句,指定一些(但不是全部)新行的自动增量值。示例如下,其中
c1
是AUTO_INCREMENT
table 的一列t1
:INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');
另一种类型的“混合模式插入”是 INSERT ... ON DUPLICATE KEY UPDATE,在最坏的情况下,实际上是 anINSERT 后跟 a UPDATE,其中为列分配的值
AUTO_INCREMENT
在更新阶段可能会也可能不会使用。
该变量有三种可能的设置 innodb_autoinc_lock_mode 。设置为 0、1 或 2,分别表示 “传统”、“连续”或 “交错”锁定模式。
innodb_autoinc_lock_mode = 0
(“传统”锁定模式)innodb_autoinc_lock_mode 传统的锁定模式提供与引入变量 之前存在的相同行为 。提供传统的锁定模式选项是为了向后兼容、性能测试以及解决由于语义上可能存在的差异而导致的“混合模式插入”问题。
在此锁定模式下,所有“类似 INSERT ”的语句都会获得特殊的表级
AUTO-INC
锁,用于插入包含AUTO_INCREMENT
列的表。该锁通常保持到语句末尾(而不是事务结束),以确保以可预测且可重复的顺序为给定的语句序列分配自动递增值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;
假设有两个事务正在运行,每个事务都将行插入到具有
AUTO_INCREMENT
列的表中。一个事务使用 INSERT ... SELECT插入 1000 行的语句,另一个事务使用 INSERT插入一行的简单语句:Tx1: INSERT INTO t1 (c2) SELECT 1000 rows from another table ... Tx2: INSERT INTO t1 (c2) VALUES ('xxx');
InnoDB
无法提前知道从 Tx1 中的SELECT语句中 检索了多少行INSERT,并且它会在语句进行时一次分配一个自动增量值。使用表级锁,一直保持到语句末尾,一次只能执行一条 INSERT引用表的语句t1
,并且不同语句生成的自增数不会交错。Tx1 语句生成的自增值 INSERT ... SELECT是连续的,并且 Tx1 语句使用的(单个)自增值 INSERTTx2 中的语句比 Tx1 中使用的所有语句小或大,具体取决于首先执行的语句。只要从二进制日志重放时(使用基于语句的复制或在恢复场景中)SQL 语句以相同的顺序执行,结果就与 Tx1 和 Tx2 第一次运行时的结果相同。因此,保持到语句结束的表级锁使得 INSERT使用自动增量的语句可以安全地用于基于语句的复制。但是,当多个事务同时执行插入语句时,这些表级锁会限制并发性和可伸缩性。
在前面的示例中,如果没有表级锁,则 INSERTTx2 中使用的自增列的值取决于语句执行的确切时间。如果 INSERTTx2 在INSERTTx1 运行时执行(而不是在其启动之前或完成之后),则这两个语句分配的特定自动增量值 INSERT是不确定的,并且可能因运行而异。
在连续锁模式 下 ,可以避免在行数已知的 “简单插入”
InnoDB
语句中使用表级AUTO-INC
锁 ,并且仍然保留基于语句的复制的确定性执行和安全性。如果您不使用二进制日志重放 SQL 语句作为恢复或复制的一部分,则 可以使用交错 锁模式来消除所有表级
AUTO-INC
锁的使用,以实现更高的并发性和性能,但代价是允许自动锁之间存在间隙。 - 递增由语句分配的数字,并且可能具有由同时执行的交错语句分配的数字。innodb_autoinc_lock_mode = 1
(“连续”锁定模式)这是默认的锁定模式。在这种模式下,“批量插入”使用特殊的
AUTO-INC
表级锁并保持它直到语句结束。这适用于所有 INSERT ... SELECT、 REPLACE ... SELECT、 和LOAD DATA语句。AUTO-INC
一次只能执行一条持有锁的语句 。如果批量插入操作的源表与目标表不同,则AUTO-INC
在对源表中选择的第一行加共享锁后,再对目标表加锁。如果批量插入操作的源和目标是同一个表,AUTO-INC
在所有选定行上获取共享锁后获取锁定。“简单插入”(预先知道要插入的行数)
AUTO-INC
通过在互斥锁(轻量级锁)的控制下获取所需数量的自动增量值来避免表级锁,该互斥锁仅在分配过程期间保持,而不是直到语句完成。除非锁被另一个事务持有,否则不会AUTO-INC
使用 表级 锁。AUTO-INC
如果另一个事务持有AUTO-INC
锁,则“简单插入”会等待AUTO-INC
锁,就好像它是“批量插入”。这种锁定模式确保,在存在 INSERT预先未知行数的语句(并且随着语句的进行而分配自动增量号)时,由任何 “ INSERT-like ” 语句分配的所有自动增量值都是连续的,并且操作对于基于语句的复制是安全的。
简而言之,这种锁定模式显着提高了可扩展性,同时可以安全地用于基于语句的复制。此外,与“传统” 锁定模式一样,任何给定语句分配的自动递增编号都是连续的。与 “传统”模式相比,任何使用自动增量的语句在语义上都没有变化,但有一个重要的 例外 。
例外情况是“混合模式插入”,其中用户
AUTO_INCREMENT
为多行“简单插入”中的某些(但不是全部)行的列提供显式值。对于此类插入,InnoDB
分配的自动增量值多于要插入的行数。然而,所有自动分配的值都是连续生成的(因此高于)最近执行的前一条语句生成的自动增量值。“多余”的数字会丢失。innodb_autoinc_lock_mode = 2
(“交错”锁定模式)在这种锁模式下,没有 “ INSERT-like ” 语句使用表级
AUTO-INC
锁,并且多个语句可以同时执行。这是最快且最具可扩展性的锁定模式,但是 当使用基于语句的复制或从二进制日志重放 SQL 语句的恢复场景时, 它并不安全。在这种锁定模式下,自动增量值保证在所有并发执行的 “INSERT类似” 语句中是唯一的并且单调递增。但是,由于多个语句可以同时生成数字(即,数字的分配在语句之间交错),因此为任何给定语句插入的行生成的值可能不连续。
如果执行的唯一语句是“简单插入”,其中要插入的行数是提前已知的,则为单个语句生成的数字不会有间隙, “混合模式插入”除外。然而,当执行“批量插入”时,任何给定语句分配的自动增量值可能存在间隙。
在复制中使用自动增量
如果您使用基于语句的复制,请设置 innodb_autoinc_lock_mode为 0 或 1,并在源及其副本上使用相同的值。innodb_autoinc_lock_mode如果您使用= 2(“交错”)或源和副本不使用相同锁定模式的配置,则 不能确保副本上的自动增量值与源上的自动增量值相同 。
如果您使用基于行或混合格式的复制,则所有自增锁模式都是安全的,因为基于行的复制对 SQL 语句的执行顺序不敏感(并且混合格式使用基于行的复制)对于基于语句的复制不安全的任何语句的复制)。
“丢失”自动增量值和序列间隙
在所有锁定模式(0、1 和 2)下,如果生成自增值的事务回滚,则这些自增值将“丢失”。自增列一旦生成值,无论 “ INSERT-like ” 语句是否完成,也无论包含的事务是否回滚,都无法回滚。这些丢失的值不会被重用。
AUTO_INCREMENT
因此,存储在表的列 中的值可能存在间隙 。为
AUTO_INCREMENT
列 指定 NULL 或 0在所有锁定模式(0、1 和 2)中,如果用户为
AUTO_INCREMENT
中的列 指定 NULL 或 0 INSERT,InnoDB
则将该行视为未指定该值,并为其生成一个新值。AUTO_INCREMENT
为列 分配负值在所有锁定模式(0、1 和 2)中,如果为列分配负值,则自动增量机制的行为是未定义的
AUTO_INCREMENT
。如果该
AUTO_INCREMENT
值大于指定整数类型的最大整数在所有锁定模式(0、1 和 2)中,如果值大于可以存储在指定整数类型中的最大整数,则自动递增机制的行为是未定义的。
“批量插入” 的自动增量值中的间隙
innodb_autoinc_lock_mode 设置为 0(“传统”)或 1(“连续” ) 时 ,任何给定语句生成的自增值都是连续的,没有间隙,因为表级
AUTO-INC
锁一直保持到语句结束,并且只一次可以执行一个这样的语句。innodb_autoinc_lock_mode 设置为 2(“ interleaved ” ) 时 , “批量插入”生成的自动增量值中可能存在间隙,但前提是同时执行 “ INSERT-like ” 语句。
对于锁定模式 1 或 2,连续语句之间可能会出现间隙,因为对于批量插入,每个语句所需的自动增量值的确切数量可能未知,并且可能会高估。
“混合模式插入” 分配的自动增量值
考虑“混合模式插入” ,其中 “简单插入”指定某些(但不是全部)结果行的自动增量值。此类语句在锁定模式 0、1 和 2 下的行为有所不同。例如,假设
c1
是AUTO_INCREMENT
table 的一列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-like ” 语句(任何类型),这个结果都是正确的。
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-like ” 语句(任何类型),这个结果都是正确的。
innodb_autoinc_lock_mode 设置为 2(“交错” ) 时 ,四个新行是:
mysql> SELECT c1, c2 FROM t1 ORDER BY c2; +-----+------+ | c1 | c2 | +-----+------+ | 1 | a | | x | b | | 5 | c | | y | d | +-----+------+
x
和 的值y
是唯一的并且大于任何先前生成的行。x
但和 的具体值y
取决于并发执行语句生成的自增值的数量。最后,考虑以下语句,当最近生成的序列号为 100 时发出:
mysql> INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (101,'c'), (NULL,'d');
对于任何 innodb_autoinc_lock_mode 设置,此语句都会生成重复键错误 23000 (
Can't write; duplicate key in table
),因为为该行分配了 101(NULL, 'b')
并且该行的插入(101, 'c')
失败。修改语句
AUTO_INCREMENT
序列中间的列值 INSERT在所有锁定模式(0、1 和 2)中,修改 语句
AUTO_INCREMENT
序列中间的列值INSERT 可能会导致“重复条目” 错误。例如,如果执行将 列值UPDATE更改AUTO_INCREMENT
为大于当前最大自动增量值的值的操作,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); ERROR 1062 (23000): Duplicate entry '4' for key 'PRIMARY'
本节介绍如何InnoDB
初始化 AUTO_INCREMENT
计数器。
如果为表指定AUTO_INCREMENT
列InnoDB
,则数据字典中的表句柄 InnoDB
包含一个称为自动增量计数器的特殊计数器,用于为该列分配新值。该计数器仅存储在主存储器中,而不存储在磁盘上。
要在服务器重新启动后初始化自动增量计数器, InnoDB
请在第一次插入包含列的表时执行与以下语句等效的语句AUTO_INCREMENT
。
SELECT MAX(ai_col) FROM table_name FOR UPDATE;
InnoDB
递增语句检索到的值并将其分配给该列和表的自动递增计数器。默认情况下,该值增加 1。此默认值可以通过 auto_increment_increment 配置设置覆盖。
如果表为空,InnoDB
则使用值 1
。此默认值可以通过配置设置覆盖 auto_increment_offset 。
如果SHOW TABLE STATUS语句在自动递增计数器初始化之前检查表,则InnoDB
初始化但不递增该值。该值被存储以供以后插入使用。此初始化在表上使用正常的独占锁定读取,并且锁定持续到事务结束。InnoDB
遵循与初始化新创建的表的自动增量计数器相同的过程。
自动递增计数器初始化后,如果没有显式指定列的值 AUTO_INCREMENT
, InnoDB
则递增计数器并将新值分配给该列。如果插入显式指定列值的行,并且该值大于当前计数器值,则计数器将设置为指定的列值。
InnoDB
只要服务器运行,就使用内存中的自动递增计数器。当服务器停止并重新启动时,如前所述, InnoDB
重新初始化每个表的第一个表的计数器 。INSERT
服务器重新启动还会取消和 语句中表选项 的效果 ,您可以将其与表一起使用来设置初始计数器值或更改当前计数器值。 AUTO_INCREMENT =
CREATE TABLEALTER TABLEN
InnoDB