Documentation Home
MySQL 8.0 参考手册  / 第 15 章 InnoDB 存储引擎  / 15.6 InnoDB 磁盘结构  / 15.6.1 表格  /  14.6.1.5 将表从 MyISAM 转换为 InnoDB

14.6.1.5 将表从 MyISAM 转换为 InnoDB

如果您有MyISAM要转换InnoDB为更好的可靠性和可扩展性的表,请在转换前查看以下指南和提示。

调整 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 未完成的更改或COMMIT 已完成的更改。

  • ROLLBACK是一个相对昂贵的操作,因为 INSERTUPDATEDELETE操作是在 之前写入InnoDB表的 COMMIT,期望大多数更改成功提交并且回滚很少。在试验大量数据时,避免对大量行进行更改,然后回滚这些更改。

  • INSERT当使用一系列语句 加载大量数据时 ,定期COMMIT获取结果以避免事务持续数小时。在数据仓库的典型加载操作中,如果出现问题,您将截断表(使用TRUNCATE TABLE)并从头开始,而不是执行 ROLLBACK.

前面的技巧可以节省在太长的事务中可能浪费的内存和磁盘空间。当事务比应有的短时,问题是 I/O 过多。对于每个 COMMIT,MySQL 确保每个更改都安全地记录到磁盘,这涉及一些 I/O。

  • 对于InnoDB表上的大多数操作,您应该使用设置 autocommit=0。从效率的角度来看,这可以避免在发出大量连续 INSERTUPDATE、 或 DELETE语句时不必要的 I/O。从安全的角度来看,如果您在mysql命令行或应用程序的异常处理程序中 出错,这允许您发出一条 ROLLBACK 语句来恢复丢失或乱码的数据。

  • autocommit=1适用于 InnoDB运行一系列查询以生成报告或分析统计信息时的表。在这种情况下,没有 COMMITor 相关的 I/O 惩罚ROLLBACKInnoDB可以 自动优化只读工作负载

  • 如果您进行了一系列相关更改,请一次完成所有更改,并 COMMIT在末尾添加一个。例如,如果您将相关的信息片段插入到多个表中,则COMMIT 在进行所有更改后执行一个。或者,如果您连续运行许多 语句,请在加载所有数据后INSERT执行一个语句 ;COMMIT如果你正在做数百万的 报表,也许通过每万或十万条记录 INSERT发出一条来拆分巨大的交易 ,这样交易就不会变得太大。COMMIT

  • 请记住,即使是一条SELECT 语句也会打开一个事务,因此在交互式mysql 会话中运行一些报告或调试查询后,发出一个COMMIT 或关闭mysql会话。

有关相关信息,请参阅 第 14.7.2.2 节,“自动提交、提交和回滚”

处理死锁

您可能会 在MySQL错误日志. 死锁 对于表来说不是一个严重的问题SHOW ENGINE INNODB STATUS,通常不需要任何纠正措施。当两个事务开始修改多个表,以不同的顺序访问表时,它们可能会达到一个状态,即每个事务都在等待另一个事务,并且都无法继续。当 启用死锁检测 (默认)时,MySQL 立即检测到这种情况并取消(回滚更小InnoDB交易,允许对方继续进行。如果使用 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_tableinnodb_file_formatinnodb_page_size变量,以及 语句的 ROW_FORMATKEY_BLOCK_SIZE子句CREATE TABLE

在您最初的实验中,最重要的设置是 innodb_file_per_table。当启用此设置时,这是 MySQL 5.6.6 的默认设置,新 表将在file-per-table 表InnoDB空间中隐式创建 。与系统表空间相比,每个表文件表空间允许在表被截断或删除时由操作系统回收磁盘空间。File-per-table 表空间还支持 Barracuda文件格式和相关功能,例如表压缩、长可变长度列的高效页外存储和大索引前缀。有关详细信息,请参阅 第 14.6.3.2 节,“File-Per-Table 表空间”InnoDB.

您还可以将InnoDB表存储在共享的通用表空间中。通用表空间支持 Barracuda 文件格式,可以包含多个表。有关详细信息,请参阅 第 14.6.3.3 节,“通用表空间”

转换现有表

要将非InnoDB表转换为 InnoDB使用ALTER TABLE

ALTER TABLE table_name ENGINE=InnoDB;
警告

不要数据库中的 MySQL 系统表 mysql从转换MyISAMInnoDB表。这是不受支持的操作。如果这样做,MySQL 不会重新启动,直到您从备份中恢复旧系统表或通过重新初始化数据目录重新生成它们(请参阅 第 2.10.1 节,“初始化数据目录”)。

克隆表的结构

您可以创建InnoDB一个 MyISAM 表的克隆表,而不是ALTER TABLE用于执行转换,以便在切换之前并排测试旧表和新表。

InnoDB创建一个具有相同列和索引定义 的空表。使用查看要使用的完整 语句。将子句 更改为。 SHOW CREATE TABLE table_name\GCREATE TABLEENGINEENGINE=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在转换过程中为表中的数据创建多个临时副本,建议您在 file-per-table 表空间中创建表,以便在删除表时可以回收磁盘空间。当 innodb_file_per_table 启用配置选项(默认)时,新创建 InnoDB的表将在 file-per-table 表空间中隐式创建。

无论是MyISAM直接转换表还是创建克隆InnoDB表,请确保在此过程中有足够的磁盘空间来容纳新旧表。 InnoDB表比表需要更多的磁盘空间MyISAM 如果一个ALTER TABLE操作用完了空间,它就会开始回滚,如果它是磁盘绑定的,这可能需要几个小时。对于insert,InnoDB使用insert buffer将二级索引记录批量合并到索引中。这节省了大量的磁盘 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 字节无符号 INTcolumn 可以索引超过 40 亿项(billion 在美国的意思是:10 亿)。对于不同整数类型的范围,请参阅 第 11.1.2 节,“整数类型(精确值)- INTEGER、INT、SMALLINT、TINYINT、MEDIUMINT、BIGINT”

了解与 InnoDB 表关联的文件

InnoDB与文件相比,文件需要更多的关注和计划MyISAM