Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


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

17.6.1.5 将 MyISAM 表转换为 InnoDB

如果您想将MyISAM表转换为InnoDB以提高可靠性和扩展性,先阅读以下指南和技巧,然后才能进行转换。

Note

MySQL 8.4之前版本创建的分区MyISAM表不兼容 MySQL 8.4。这些表必须在升级前准备好,或者将其转换为InnoDB。请参阅第26.6.2节,“与存储引擎相关的分区限制”,了解更多信息。

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

因为MyISAM表不支持事务autocommit配置选项和COMMITROLLBACK语句。这些关键字对于允许多个会话同时读写InnoDB表,提供了写入繁重工作负载的可扩展性好处。

在事务打开时,系统会保存事务开始时的数据快照,这可能会导致如果系统插入、更新和删除数百万行记录而事务还未关闭的情况下产生大量开销,因此要避免长时间运行的事务:

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

  • 确保您的应用程序中的错误处理器也ROLLBACK未完成的更改或COMMIT已完成的更改。

  • ROLLBACK 操作相对较为昂贵,因为INSERTUPDATEDELETE 操作在提交COMMIT 之前写入到 InnoDB 表中,以期望大多数更改都能成功提交,回滚很少。对大规模数据进行实验时,避免对大量行进行更改,然后回滚这些更改。

  • 在使用序列INSERT 语句加载大规模数据时,定期COMMIT 结果,以避免长时间的事务。如果在数据仓库的常规装载操作中出现问题,你可以使用TRUNCATE TABLE truncate 表,然后从头开始重新装载,而不是执行ROLLBACK

这些提示可以节省内存和磁盘空间,避免太长的事务中浪费的资源。当事务较短时,问题是过多的I/O。每个COMMIT,MySQL 都会确保每个更改安全地记录到磁盘,这涉及一些I/O。

  • 对于大多数InnoDB表的操作,您应该使用设置autocommit=0。从效率角度看,这避免了在连续执行大量INSERTUPDATEDELETE语句时不必要的I/O。从安全角度看,这允许您在命令行mysql或应用程序中的异常处理器中回滚错误数据。

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

  • 如果您做了一系列相关的更改,最后一次性提交所有更改使用单个COMMIT语句。例如,如果您将多个表中相关信息插入,做完所有更改后执行单个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子句CREATE TABLE语句。

在初期实验中,最重要的设置是innodb_file_per_table。当启用该设置时,这是默认值,新的InnoDB表隐式地在文件-per-table表空间中创建。在与InnoDB系统表空间相比,文件-per-table 表空间允许操作系统在删除或截断表时回收磁盘空间。文件-per-table 表空间还支持DYNAMICCOMPRESSED行格式,以及相关的功能,如表压缩、长变长列的高效存储和大索引前缀。更多信息,请参见第17.6.3.2节,“文件-per-table 表空间”

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

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

ALTER TABLE table_name ENGINE=InnoDB;

您可能会创建一个InnoDB表,该表是MyISAM表的克隆,而不是使用ALTER TABLE来进行转换,以便在切换前测试老和新表。

创建一个空的InnoDB表,具有相同的列和索引定义。使用CREATE TABLE语句来查看完整语句。将ENGINE子句更改为ENGINE=INNODB

将大量数据导入一个空的InnoDB表,使用以下语句:INSERT INTO innodb_table SELECT * FROM myisam_table ORDER BY primary_key_columns.

你也可以在插入数据后创建InnoDB表的索引。历史上,创建新secondary索引对InnoDB来说是一个慢操作,但现在你可以在数据加载完成后以较小的开销来创建索引。

如果你在secondary键上有UNIQUE约束,可以通过临时关闭唯一性检查来加速表导入操作:

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

对于大表,这样可以减少磁盘I/O,因为InnoDB可以使用change buffer批量写secondary索引记录。确保数据中没有重复键。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 表需要更多磁盘空间-than MyISAM 表。 如果ALTER TABLE操作耗尽了磁盘空间,它将开始回滚,并且如果是磁盘瓶颈可能需要几个小时。如果是插入操作,InnoDB 使用插入缓冲区来批量合并次要索引记录到索引中,这样可以减少磁盘I/O。对于回滚操作,没有这样的机制,回滚可能需要30倍于插入的时间。

如果出现了 runaway 回滚,如果您数据库中没有重要数据,可能要考虑杀死数据库进程,而不是等待数百万次磁盘I/O操作完成。完整过程请见第17.20.3节,“强制InnoDB恢复”

PRIMARY KEY take clause 是 MySQL 查询性能和表和索引空间使用的关键因素。主键唯一标识一行记录。每个表都应该有一个主键值,且两个行不能拥有相同的主键值。

以下是关于主键的指导原则,后面还将提供更详细的解释。

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

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

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

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

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

考虑在没有主键的表中添加一个主键。根据表的最大项目大小,使用最小的实践数字类型。这可以使每行数据更紧凑,特别是对于大型表,可以产生大量的空间节省。如果表中有次要索引,那么主键值在每个次要索引条目中重复出现。除了减少磁盘上的数据大小,一个小的主键也可以让更多数据进入缓冲池,加速所有操作并改善并发性。

如果表已经有了某个更长的列作为主键,例如VARCHARAUTO_INCREMENT列,并将主键切换到该列,即使该列在查询中不被引用。这种设计变更可以在次要索引中产生大量的空间节省。你可以将原来的主键列设为UNIQUE NOT NULL,以强制相同约束,即防止这些列中的重复或空值。

如果您将相关信息分散到多个表中,通常每个表都使用同一个列作为主键。例如,人力资源数据库可能有几个表,每个表的主键都是员工号。销售数据库可能有某些表的主键是客户号,其他表的主键是订单号。因为主键查找非常快,您可以构建高效的连接查询语句。

如果您完全不指定PRIMARY KEY子句,MySQL 会自动创建一个不可见的主键。它是一个 6 字节值,可能比你需要的长,因此浪费空间。因为它是隐藏的,所以不能在查询中引用。

InnoDB 的可靠性和扩展性特征需要更多的磁盘存储空间,而等效的 MyISAM 表则不需要。您可以修改列和索引定义,以便更好地利用空间,减少 I/O 和内存消耗,当处理结果集时提高查询优化计划的效率。

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

理解与InnoDB相关的文件

InnoDB 文件需要比 MyISAM 文件更为小心和计划。