Documentation Home
MySQL 8.3 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

MySQL 8.3 Reference Manual  /  ...  /  Converting Tables from MyISAM to InnoDB

17.6.1.5 将表从 MyISAM 转换为 InnoDB

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

Note

在之前的 MySQL 版本中创建的分区 MyISAM 表格与 MySQL 8.3 不兼容。这些表格必须在升级前进行准备,或者删除分区,或者将其转换为 InnoDB。请参阅 第 26.6.2 节,“与存储引擎相关的分区限制”,以获取更多信息。

调整 MyISAM 和 InnoDB 的内存使用

当您从 MyISAM 表迁移到其他表时,降低 key_buffer_size 配置选项的值,以释放不再需要的缓存结果的内存。增加 innodb_buffer_pool_size 配置选项的值,该选项为 InnoDB 表分配缓存内存,类似于缓存结果。 InnoDB缓冲池 缓存表数据和索引数据,提高查询速度,并将查询结果保留在内存中以便重用。有关缓冲池大小配置的指导,请参阅 第 10.12.3.1 节,“MySQL 如何使用内存”

处理过长或过短的事务

因为 MyISAM 表不支持 事务,您可能没有注意到 autocommit 配置选项和 COMMITROLLBACK 语句。这些关键字对于允许多个会话并发读取和写入 InnoDB 表非常重要,提供了大量的可扩展性优势在写入密集型工作负载中。

当事务打开时,系统会保留事务开始时的数据快照,这可能会导致大量的开销,如果系统在事务运行时插入、更新和删除了数百万行数据。因此,请避免事务运行太长时间:

  • 如果您使用 mysql 会话进行交互式实验,总是 COMMIT(以确认更改)或 ROLLBACK(以撤销更改)完成时。关闭交互式会话,而不是让它们长时间保持打开,以避免意外地保持事务打开太长时间。

  • 确保应用程序中的任何错误处理程序也回滚未完成的更改或提交已完成的更改。

  • 回滚是一个相对昂贵的操作,因为插入更新删除操作都写入了InnoDB表之前的提交,以期望大多数更改都能成功提交,而回滚则很少。当实验大批量数据时,避免对大量行进行更改然后回滚这些更改。

  • 当使用一系列插入语句加载大量数据时,周期性地提交结果,以避免事务持续数小时。在典型的数据仓库加载操作中,如果出现问题,truncate 表(使用TRUNCATE TABLE)并从头开始,而不是执行回滚

前面的提示可以节省内存和磁盘空间,这些空间可能在太长的事务中被浪费。当事务太短时,问题是过多的I/O。每次提交,MySQL 都会确保每个更改都安全地记录到磁盘上,这涉及到一些I/O。

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

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

  • 如果您进行了一系列相关的更改,请在最后使用单个 COMMIT 来_finalize 所有更改。例如,如果您将相关的信息 piece 插入到多个表中,请在所有更改后执行单个 COMMIT。或者,如果您运行了许多连续的 INSERT 语句,请在所有数据加载完成后执行单个 COMMIT;如果您正在执行数百万个 INSERT 语句,可能需要每隔十万或一百万记录执行一个 COMMIT,以便事务不至于变得太大。

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

有关信息,请参阅 第 17.7.2.2 节,“autocommit, Commit, and Rollback”

处理死锁

您可能会在 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输出中看到最后一个警告。

有关更多信息,请参阅第17.7.5节,“InnoDB中的死锁”

存储布局

要从InnoDB表中获得最佳性能,可以调整与存储布局相关的一些参数。

当你将大型、频繁访问的、包含重要数据的MyISAM表转换为InnoDB表时,调查和考虑innodb_file_per_tableinnodb_page_size变量,以及ROW_FORMATKEY_BLOCK_SIZE子句。

在初始实验中,最重要的设置是innodb_file_per_table。启用该设置时,默认情况下,新的InnoDB表将隐式创建在每个表文件表空间中。与InnoDB系统表空间相比,每个表文件表空间允许操作系统在表被截断或删除时回收磁盘空间。此外,每个表文件表空间还支持动态行格式压缩行格式,以及相关功能,如表压缩、长变长列的高效离页存储和大索引前缀。有关更多信息,请参阅第17.6.3.2节,“每个表文件表空间”

您也可以将 InnoDB 表存储在共享的通用表空间中,该表空间支持多个表和所有行格式。有关更多信息,请参阅 第 17.6.3.3 节,“通用表空间”

将现有表转换

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

ALTER TABLE table_name ENGINE=InnoDB;
克隆表结构

您可能想创建一个 InnoDB 表,该表是 MyISAM 表的克隆,而不是使用 ALTER TABLE 进行转换,以便在转换前后同时测试旧表和新表。

创建一个空的 InnoDB 表,具有相同的列和索引定义。使用 SHOW CREATE TABLE table_name\G 查看完整的 CREATE TABLE 语句以供使用。将 ENGINE 子句更改为 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 表中创建多个临时副本数据during the conversion process,那么建议在文件每个表空间中创建表,以便在删除表时可以回收磁盘空间。当 innodb_file_per_table 配置选项启用时(默认情况下),新创建的 InnoDB 表隐式地在文件每个表空间中创建。

无论您是直接将 MyISAM 表转换还是创建克隆的 InnoDB 表,确保您有足够的磁盘空间来容纳旧表和新表的过程。InnoDB 表需要比 MyISAM 表更多的磁盘空间。 如果 ALTER TABLE 操作耗尽空间,它将开始回滚,并且如果是磁盘绑定的情况下,回滚可能需要数小时。对于插入,InnoDB 使用插入缓冲区将次要索引记录批量写入索引中,从而节省了大量磁盘I/O。对于回滚,没有这样的机制,回滚可能需要30倍于插入的时间。

在运行-away 回滚的情况下,如果您数据库中没有重要数据,可能需要杀死数据库进程,而不是等待数百万个磁盘I/O 操作完成。有关完整的过程,请参阅 第 17.20.3 节,“强制 InnoDB 恢复”

定义主键

主键子句是影响 MySQL 查询性能和表格和索引空间使用的关键因素。主键唯一标识表中的行。每行都应该有一个主键值,并且不能有两个行具有相同的主键值。

以下是主键的指导方针,后面是更详细的解释。

  • 为每个表声明一个 PRIMARY KEY。通常,它是您在 WHERE 子句中查找单行时最重要的列。

  • 在原始的 CREATE TABLE 语句中声明 PRIMARY KEY 子句,而不是通过 ALTER TABLE 语句添加它。

  • 小心选择列和其数据类型。优先选择数字列而不是字符或字符串列。

  • 如果没有其他稳定、唯一、非空、数字列,可以考虑使用自动递增列。

  • 自动递增列也是一个不错的选择,如果主键列的值可能会改变。更改主键列的值是一个昂贵的操作,可能涉及到重新排列表中的数据和每个次要索引中的数据。

考虑添加一个 主键 到任何没有主键的表中。使用基于表最大预计大小的最小实用数字类型。这可以使每行变得更加紧凑,从而为大表节省大量空间。这种空间节省还会因为次要索引中的主键值重复而被放大。此外,小主键还可以让更多数据适合 缓冲池,从而加速所有操作和改善并发性。

如果表已经在某个较长的列上有主键,例如 VARCHAR,可以考虑添加一个新的无符号 AUTO_INCREMENT 列,并将主键切换到该列,即使该列不在查询中被引用。这种设计更改可以在次要索引中产生实质性的空间节省。您可以将前一个主键列指定为 UNIQUE NOT NULL,以强制相同的约束条件,如防止重复或空值跨所有这些列。

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

如果您完全省略 PRIMARY KEY 子句,MySQL 将为您创建一个不可见的主键。它是一个 6 字节的值,可能比您需要的更长,从而浪费空间。因为它是隐藏的,因此您无法在查询中引用它。

应用程序性能考虑因素

InnoDB 的可靠性和可扩展性功能比等效的 MyISAM 表需要更多的磁盘存储空间。您可能需要对列和索引定义进行一些修改,以便更好地利用空间,减少 I/O 和内存消耗,并生成更高效的查询优化计划,以便更好地使用索引查找。

如果您设置了一个数字 ID 列作为主键,请使用该值来与其他表中的相关值进行交叉引用,特别是在 连接 查询中。例如,而不是接受国家名称作为输入并执行查询来搜索相同的名称,请执行一次查找以确定国家 ID,然后执行其他查询(或单个连接查询)以在多个表中查找相关信息。相比之下,将客户或目录项目编号存储为字符串数字,可能占用多个字节,请将其转换为数字 ID 进行存储和查询。一个 4 字节无符号 INT 列可以索引超过 40 亿个项目(以美国的亿为单位:1000 万)。有关不同整数类型的范围,请参阅 第 13.1.2 节,“整数类型(精确值)- INTEGER、INT、SMALLINT、TINYINT、MEDIUMINT、BIGINT”

InnoDB 表关联文件的理解

InnoDB 文件需要比 MyISAM 文件更多的关心和规划。