#### 15.6.1.5 将表从 MyISAM 转换为 InnoDB

[](<>)[](<>)

如果你有[`MyISAM`](myisam-storage-engine.html)要转换为的表[`InnoDB`](innodb-storage-engine.html)为了获得更好的可靠性和可扩展性，请在转换前查看以下指南和提示。

笔记

分区`MyISAM`在以前版本的 MySQL 中创建的表与 MySQL 8.0 不兼容。此类表必须在升级之前准备好，通过删除分区或将它们转换为`InnoDB`.看[第 24.6.2 节，“与存储引擎相关的分区限制”](partitioning-limitations-storage-engines.html)， 了解更多信息。

-   [调整 MyISAM 和 InnoDB 的内存使用情况](converting-tables-to-innodb.html#innodb-convert-memory-usage)

-   [处理过长或过短的事务](converting-tables-to-innodb.html#innodb-convert-transactions)

-   [处理死锁](converting-tables-to-innodb.html#innodb-convert-deadlock)

-   [存储布局](converting-tables-to-innodb.html#innodb-convert-plan-storage)

-   [转换现有表](converting-tables-to-innodb.html#innodb-convert-convert)

-   [克隆表的结构](converting-tables-to-innodb.html#innodb-convert-clone)

-   [传输数据](converting-tables-to-innodb.html#innodb-convert-transfer)

-   [存储要求](converting-tables-to-innodb.html#innodb-convert-storage-requirements)

-   [定义主键](converting-tables-to-innodb.html#innodb-convert-primary-key)

-   [应用程序性能注意事项](converting-tables-to-innodb.html#innodb-convert-application-performance)

-   [了解与 InnoDB 表关联的文件](converting-tables-to-innodb.html#innodb-convert-understand-files)

##### 调整 MyISAM 和 InnoDB 的内存使用

[](<>)

当你从`MyISAM`表，降低的价值[`key_buffer_size`](server-system-variables.html#sysvar_key_buffer_size)配置选项来释放缓存结果不再需要的内存。增加价值[`innodb_buffer_pool_size`](innodb-parameters.html#sysvar_innodb_buffer_pool_size)配置选项，它执行类似的分配缓存内存的作用`InnoDB`表。这`InnoDB` [缓冲池](glossary.html#glos_buffer_pool)缓存表数据和索引数据，加快查询查找速度并将查询结果保存在内存中以供重用。有关缓冲池大小配置的指导，请参阅[第 8.12.3.1 节，“MySQL 如何使用内存”](memory-use.html).

##### 处理过长或过短的事务

[](<>)

因为`MyISAM`表不支持[交易](glossary.html#glos_transaction)，你可能没有太注意[`自动提交`](server-system-variables.html#sysvar_autocommit)配置选项和[`犯罪`](commit.html)和[`回滚`](commit.html)陈述。这些关键字对于允许多个会话进行读写很重要`InnoDB`表并发，在写入繁重的工作负载中提供显着的可扩展性优势。

当事务打开时，系统会保留事务开始时所见数据的快照，如果系统在杂散事务继续运行时插入、更新和删除数百万行，这可能会导致大量开销。因此，请注意避免运行时间过长的事务：

-   如果您使用的是[**mysql**](mysql.html)交互式实验会话，总是[`犯罪`](commit.html)（以完成更改）或[`回滚`](commit.html)（撤消更改）完成后。关闭交互式会话，而不是让它们长时间打开，以避免意外保持交易长时间打开。

-   确保您的应用程序中的任何错误处理程序也[`回滚`](commit.html)不完整的更改或[`犯罪`](commit.html)完成的更改。

-   [`回滚`](commit.html)是一项相对昂贵的操作，因为[`插入`](insert.html),[`更新`](update.html)， 和[`删除`](delete.html)操作被写入`InnoDB`之前的表[`犯罪`](commit.html)，期望大多数更改都成功提交并且很少回滚。在试验大量数据时，避免对大量行进行更改然后回滚这些更改。

-   当加载大量数据时[`插入`](insert.html)定期声明[`犯罪`](commit.html)避免交易持续数小时的结果。在数据仓库的典型加载操作中，如果出现问题，您会截断表（使用[`截断表`](truncate-table.html)) 并从头开始而不是做[`回滚`](commit.html).

    前面的提示可以节省在太长的事务期间可能浪费的内存和磁盘空间。当事务比应有的短时，问题是 I/O 过多。与每个[`犯罪`](commit.html), MySQL 确保每个更改都安全地记录到磁盘，这涉及一些 I/O。

-   对于大多数操作`InnoDB`表，您应该使用设置[`自动提交=0`](server-system-variables.html#sysvar_autocommit).从效率的角度来看，这避免了当您发出大量连续的 I/O[`插入`](insert.html),[`更新`](update.html)， 要么[`删除`](delete.html)陈述。从安全的角度来看，这允许您发出[`回滚`](commit.html)如果您在[**mysql**](mysql.html)命令行或应用程序的异常处理程序中。

-   [`自动提交=1`](server-system-variables.html#sysvar_autocommit)适用于`InnoDB`运行一系列查询以生成报告或分析统计信息时的表。在这种情况下，没有与相关的 I/O 损失[`犯罪`](commit.html)要么[`回滚`](commit.html)， 和`InnoDB`能够[自动优化只读工作负载](innodb-performance-ro-txn.html).

-   如果您进行了一系列相关更改，请一次性完成所有更改[`犯罪`](commit.html)在末尾。例如，如果您将相关的信息片段插入到多个表中，请执行一个[`犯罪`](commit.html)进行所有更改后。或者，如果您连续运行多次[`插入`](insert.html)语句，做一个[`犯罪`](commit.html)加载完所有数据后；如果你正在做数百万[`插入`](insert.html)声明，也许通过发出一个[`犯罪`](commit.html)每一万或十万条记录，因此事务不会增长太大。

-   请记住，即使是[`选择`](select.html)语句打开一个事务，因此在交互式运行一些报告或调试查询之后[**mysql**](mysql.html)会话，要么发出[`犯罪`](commit.html)或关闭[**mysql**](mysql.html)会议。

    有关相关信息，请参阅[第 15.7.2.2 节，“自动提交、提交和回滚”](innodb-autocommit-commit-rollback.html).

##### 处理死锁

[](<>)

您可能会在 MySQL 错误日志中看到有关“死锁”的警告消息，或者[`显示引擎 INNODB 状态`](show-engine.html).一种[僵局](glossary.html#glos_deadlock)不是一个严重的问题`InnoDB`表，并且通常不需要任何纠正措施。当两个事务开始修改多个表，以不同的顺序访问这些表时，它们可能会达到一个状态，即每个事务都在等待另一个事务，并且两者都不能继续。什么时候[死锁检测](glossary.html#glos_deadlock_detection)启用（默认），MySQL 会立即检测到这种情况并取消（[回滚](glossary.html#glos_rollback)) “较小”的交易，允许其他交易继续进行。如果使用禁用死锁检测[`innodb_deadlock_detect`](innodb-parameters.html#sysvar_innodb_deadlock_detect)配置选项，`InnoDB`依赖于[`innodb_lock_wait_timeout`](innodb-parameters.html#sysvar_innodb_lock_wait_timeout)设置为在死锁的情况下回滚事务。

无论哪种方式，您的应用程序都需要错误处理逻辑来重新启动由于死锁而被强制取消的事务。当您重新发出与以前相同的 SQL 语句时，原来的计时问题不再适用。其他交易已经完成并且您的交易可以继续，或者其他交易仍在进行中并且您的交易一直等到它完成。

如果死锁警告不断出现，您可以查看应用程序代码以以一致的方式重新排序 SQL 操作，或缩短事务。你可以用[`innodb_print_all_deadlocks`](innodb-parameters.html#sysvar_innodb_print_all_deadlocks)启用选项以查看 MySQL 错误日志中的所有死锁警告，而不是仅查看[`显示引擎 INNODB 状态`](show-engine.html)输出。

有关详细信息，请参阅[第 15.7.5 节，“InnoDB 中的死锁”](innodb-deadlocks.html).

##### 存储布局

[](<>)

为了获得最佳性能`InnoDB`表，您可以调整一些与存储布局相关的参数。

当你转换`MyISAM`大型、经常访问并保存重要数据的表，调查并考虑[`innodb_file_per_table`](innodb-parameters.html#sysvar_innodb_file_per_table)和[`innodb_page_size`](innodb-parameters.html#sysvar_innodb_page_size)变量，以及[`ROW_FORMAT`和`KEY_BLOCK_SIZE`条款](innodb-row-format.html)的[`创建表`](create-table.html)陈述。

在您最初的实验中，最重要的设置是[`innodb_file_per_table`](innodb-parameters.html#sysvar_innodb_file_per_table).启用此设置时，这是默认设置，新`InnoDB`表是隐式创建的[每表文件](glossary.html#glos_file_per_table)表空间。与`InnoDB`system tablespace, file-per-table 表空间允许操作系统在表被截断或删除时回收磁盘空间。File-per-table 表空间也支持[动态的](glossary.html#glos_dynamic_row_format)和[压缩](glossary.html#glos_compressed_row_format)行格式和相关功能，例如表压缩、长可变长度列的高效页外存储和大索引前缀。有关详细信息，请参阅[第 15.6.3.2 节，“File-Per-Table 表空间”](innodb-file-per-table-tablespaces.html).

您还可以存储`InnoDB`共享通用表空间中的表，支持多个表和所有行格式。有关详细信息，请参阅[第 15.6.3.3 节，“通用表空间”](general-tablespaces.html).

##### 转换现有表

转换一个非`InnoDB`要使用的表`InnoDB`采用[`更改表`](alter-table.html)：

```
ALTER TABLE table_name ENGINE=InnoDB;
```

##### 克隆表的结构

你可能会做一个`InnoDB`表是 MyISAM 表的克隆，而不是使用[`更改表`](alter-table.html)执行转换，在切换之前并排测试新旧表。

创建一个空`InnoDB`具有相同列和索引定义的表。采用`显示创建表 *`表名`*\G`看到完整的[`创建表`](create-table.html)要使用的语句。更改`引擎`条款`引擎=INNODB`.

##### 传输数据

[](<>)

将大量数据传输到空`InnoDB`如上一节所示创建的表，插入行`插入 *`innodb_table`* 选择 * 从 *`myisam_table`* 订购人 *`主键列`*`.

您还可以为`InnoDB`插入数据后的表。从历史上看，创建新的二级索引是一项缓慢的操作`InnoDB`，但现在您可以在加载数据后创建索引，而索引创建步骤的开销相对较小。

如果你有`独特`对辅助键的约束，您可以通过在导入操作期间暂时关闭唯一性检查来加快表导入：

```
SET unique_checks=0;
... import operation ...
SET unique_checks=1;
```

对于大表，这可以节省磁盘 I/O，因为`InnoDB`可以使用它的[更改缓冲区](glossary.html#glos_change_buffer)将二级索引记录作为批处理写入。确保数据不包含重复键。[`unique_checks`](server-system-variables.html#sysvar_unique_checks)允许但不要求存储引擎忽略重复键。

为了更好地控制插入过程，您可以分段插入大表：

```
INSERT INTO newtable SELECT * FROM oldtable
   WHERE yourkey > something AND yourkey <= somethingelse;
```

插入所有记录后，您可以重命名表。

在大表的转换过程中，增加`InnoDB`缓冲池以减少磁盘 I/O。通常，推荐的缓冲池大小为系统内存的 50% 到 75%。您还可以增加`InnoDB`日志文件。

##### 存储要求

[](<>)

如果您打算在`InnoDB`在转换过程中，建议您在 file-per-table 表空间中创建表，以便在删除表时可以回收磁盘空间。当。。。的时候[`innodb_file_per_table`](innodb-parameters.html#sysvar_innodb_file_per_table)配置选项已启用（默认），新创建`InnoDB`表是在 file-per-table 表空间中隐式创建的。

无论您转换`MyISAM`直接表或创建一个克隆`InnoDB`表，请确保您有足够的磁盘空间在此过程中同时保存旧表和新表。**`InnoDB`表需要的磁盘空间比`MyISAM`表。**如果[`更改表`](alter-table.html)操作空间不足，它开始回滚，如果它是磁盘绑定的，则可能需要数小时。对于插入，`InnoDB`使用插入缓冲区将二级索引记录批量合并到索引中。这节省了大量的磁盘 I/O。对于回滚，没有使用这种机制，回滚可能需要比插入长 30 倍的时间。

在失控回滚的情况下，如果您的数据库中没有有价值的数据，建议终止数据库进程而不是等待数百万磁盘 I/O 操作完成。完整的过程，请参见[第 15.21.3 节，“强制 InnoDB 恢复”](forcing-innodb-recovery.html).

##### 定义主键

[](<>)

这`首要的关键`子句是影响 MySQL 查询性能以及表和索引空间使用的关键因素。主键唯一标识表中的一行。表中的每一行都应该有一个主键值，并且没有两行可以有相同的主键值。

这些是主键的指导方针，然后是更详细的解释。

-   声明一个`首要的关键`对于每张桌子。通常，它是您在`在哪里`查找单行时的子句。

-   声明`首要的关键`原文中的条款[`创建表`](create-table.html)声明，而不是稍后通过[`更改表`](alter-table.html)陈述。

-   仔细选择列及其数据类型。首选数字列而不是字符或字符串列。

-   如果没有另一个稳定的、唯一的、非空的数字列可供使用，请考虑使用自动增量列。

-   如果不确定主键列的值是否会改变，自动增量列也是一个不错的选择。更改主键列的值是一项昂贵的操作，可能涉及重新排列表内和每个二级索引内的数据。

    考虑添加一个[首要的关键](glossary.html#glos_primary_key)到任何还没有桌子的桌子。根据表格的最大投影大小使用最小的实用数值类型。这可以使每一行稍微更紧凑，这可以为大型表节省大量空间。如果表有任何空间节省成倍增加[二级索引](glossary.html#glos_secondary_index)，因为主键值在每个二级索引条目中都是重复的。除了减少磁盘上的数据大小外，小的主键还可以让更多数据放入[缓冲池](glossary.html#glos_buffer_pool)，加快各种操作，提高并发性。

    如果表在某个较长的列上已经有一个主键，例如`VARCHAR`，考虑添加一个新的无符号`自动递增`列并将主键切换到该列，即使查询中未引用该列。这种设计更改可以在二级索引中节省大量空间。您可以将以前的主键列指定为`唯一非空`强制执行与`首要的关键`子句，即防止所有这些列中出现重复值或空值。

    如果您将相关信息分布在多个表中，通常每个表都使用相同的列作为其主键。例如，一个人事数据库可能有几个表，每个表都有一个员工编号的主键。一个销售数据库可能有一些表的主键是客户编号，而其他表的主键是订单号。由于使用主键的查找速度非常快，因此您可以为此类表构建高效的连接查询。

    如果你离开`首要的关键`完全退出，MySQL 会为您创建一个隐形的。它是一个 6 字节的值，可能比您需要的要长，因此会浪费空间。因为它是隐藏的，所以您不能在查询中引用它。

##### 应用程序性能注意事项

[](<>)

的可靠性和可扩展性特点`InnoDB`需要比同等设备更多的磁盘存储空间`MyISAM`表。您可以稍微更改列和索引定义，以获得更好的空间利用率，减少处理结果集时的 I/O 和内存消耗，以及更好的查询优化计划以有效利用索引查找。

如果您为主键设置数字 ID 列，请使用该值与任何其他表中的相关值交叉引用，特别是对于[加入](glossary.html#glos_join)查询。例如，与其接受国家名称作为输入并执行查询以搜索相同的名称，不如执行一次查找以确定国家 ID，然后执行其他查询（或单个连接查询）以跨多个表查找相关信息。与其将客户或目录项目编号存储为可能会占用多个字节的数字字符串，不如将其转换为数字 ID 以进行存储和查询。一个 4 字节的无符号[`INT`](integer-types.html)列可以索引超过 40 亿个项目（美国的含义是十亿：10 亿）。有关不同整数类型的范围，请参阅[第 11.1.2 节，“整数类型（精确值） - INTEGER、INT、SMALLINT、TINYINT、MEDIUMINT、BIGINT”](integer-types.html).

##### 了解与 InnoDB 表关联的文件

[](<>)

`InnoDB`文件需要更多的关注和计划，而不是`MyISAM`文件做。

-   您不得删除[ibdata 文件](glossary.html#glos_ibdata_file)代表`InnoDB` [系统表空间](glossary.html#glos_system_tablespace).

-   移动或复制的方法`InnoDB`到不同服务器的表在[第 15.6.1.4 节，“移动或复制 InnoDB 表”](innodb-migration.html).
