MySQL 5.7 将表从MyISAM存储引擎转换为InnoDB存储引擎

将表从MyISAM存储引擎转换为InnoDB存储引擎
如果你想将MyISAM表转换为InnoDB,以获得更好的可靠性和可伸缩性,那么在转换之前,请查看以下指南和技巧。
.调整MyISAM和InnoDB的内存使用
.处理过长或过短的事务
.处理死锁
.规划存储布局
.转换现有表
.克隆表的结构
.传送现有数据
.储存需求
.为每个表定义一个主键
.应用程序性能考虑
.了解与InnoDB表关联的文件

调整MyISAM和InnoDB的内存使用
当用户将MyISAM表转换为InnoDB表时,降低key_buffer_size配置选项的值,以释放缓存结果不再需要的内存。增加innodb_buffer_pool_size配置选项的值,该选项的作用类似于为InnoDB表分配缓存内存。InnoDB缓冲池缓存了表数据和索引数据,加快了查询的查找速度,并将查询结果保存在内存中以供重用。

在繁忙的服务器上,在关闭查询缓存的情况下运行基准测试。InnoDB缓冲池提供了类似的好处,所以查询缓存可能不必要地占用内存。

处理过长或过短的事务
因为MyISAM表不支持事务,所以您可能没有太注意自动提交配置选项以及COMMIT和ROLLBACK语句。这些关键字对于允许多个会话并发地读写InnoDB表非常重要,在写工作负载繁重的情况下提供大量的可伸缩性优势。

当事务处于打开状态时,系统会保留事务开始时看到的数据快照,如果系统插入、更新和删除数百万行,而事务继续运行,这可能会导致大量开销。因此,要注意避免事务运行时间过长:

.如果您使用mysql会话进行交互实验,当完成后请始终COMMIT(完成更改)或ROLLBACK(撤销更改)。关闭交互式会话,而不是让它们长时间开放,以避免意外地使事务长时间开放。

.确保应用程序中的任何错误处理程序也ROLLBACK未完成的更改或COMMIT已完成的更改。

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

.当使用INSERT语句顺序加载大量数据时,请定期提交结果,以避免事务持续数小时。在典型的数据仓库加载操作中,如果出现问题,您将截断表(使用truncate table)并从头开始,而不是执行ROLLBACK。

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

.对于大多数InnoDB表的操作,你应该设置autocommit=0。从效率的角度来看,当发出大量连续的INSERT、UPDATE或DELETE语句时,这避免了不必要的I/O。从安全的角度来看,如果你在mysql命令行或应用程序的异常处理中犯了错误,这允许你发出回滚语句来恢复丢失或混淆的数据。

.autocommit=1适合InnoDB表的情况是,当运行一系列查询以生成报告或分析统计数据时。在这种情况下,不存在与COMMIT或ROLLBACK相关的I/O惩罚, InnoDB可以自动优化只读工作负载。

.如果您进行了一系列相关的更改,请在最后使用COMMIT命令一次完成所有更改。例如,如果将相关的信息片段插入到几个表中,则在进行所有更改后执行一次COMMIT。或者,如果您运行了许多连续的INSERT语句,请在加载完所有数据后执行单个COMMIT;如果您要执行数百万条INSERT语句,也许可以通过每10,000或100,000条记录发出COMMIT来拆分巨大的事务,这样事务就不会变得太大。

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

处理死锁
你可能会在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输出中的最后一个警告。

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

当您转换大型、频繁访问和保存重要数据的MyISAM表时,请调查并考虑innodb_file_per_table、innodb_file_format和innodb_page_size配置选项,以及CREATE TABLE语句的ROW_FORMAT和KEY_BLOCK_SIZE子句。

在最初的实验中,最重要的设置是innodb_file_per_table。当启用这个设置时,MySQL 5.6.6默认会在file-per–table表空间中隐式创建新的InnoDB表。与InnoDB系统表空间不同的是,在表被截断或删除时,操作系统可以通过file-per-table表空间回收磁盘空间。file-per-table表空间还支持Barracuda文件格式和相关特性,如表压缩、变长列的高效页外存储和大索引前缀。

您也可以将InnoDB表存储在共享的通用表空间中。通用表空间支持Barracuda文件格式,可以包含多个表。

转换现有表
使用ALTER table命令将一个非InnoDB表转换为InnoDB表。
ALTER TABLE table_name ENGINE=InnoDB;

不要将MySQL数据库中的MySQL系统表从MyISAM转换为InnoDB类型。这是一个不受支持的操作。类型。

克隆表的结构
你可能会为一个MyISAM表克隆一个InnoDB表,而不是使用ALTER table执行转换,以便在切换之前并行测试新旧表。

用相同的列和索引定义创建一个空的InnoDB表。使用SHOW CREATE TABLE table_name\G查看完整的CREATE TABLE语句。将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,最大不超过物理内存的80%。你也可以增加InnoDB日志文件的大小。

储存需求
如果您打算在转换过程中为InnoDB表中的数据创建多个临时副本,建议您以文件/表的方式创建表,以便在删除表时回收磁盘空间。当innodb_file_per_table配置选项被启用(默认)时,新创建的InnoDB表将隐式地创建在file-per-table表空间中。

无论您是直接转换MyISAM表还是创建一个克隆的InnoDB表,都要确保在转换过程中有足够的磁盘空间来容纳新旧表。InnoDB表比MyISAM表需要更多的磁盘空间。如果ALTER TABLE操作耗尽了空间,它将启动回滚,如果是磁盘绑定,则回滚可能需要数小时。对于插入,InnoDB使用insert buffer将辅助索引记录批量合并到索引中。这节省了大量的磁盘I/O。对于回滚,没有使用这种机制,回滚可能比插入时间长30倍。

在回滚失控的情况下,如果您的数据库中没有有价值的数据,那么建议终止数据库进程,而不是等待数百万个磁盘I/O操作完成。

为每个表定义一个主键
主键子句是影响MySQL查询性能以及表和索引空间使用的一个关键因素。主键唯一地标识表中的一行。表中的每一行都必须有一个主键值,并且任何两行都不能有相同的主键值。

这些是主键的指导原则,后面是更详细的解释。
.为每个表声明一个主键。通常,在WHERE子句中查询单行时,它是最重要的一列。

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

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

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

.如果怀疑主键列的值是否会改变,自动递增列也是一个不错的选择。更改主键列的值是一项代价高昂的操作,可能涉及重新安排表内和每个辅助索引内的数据。

考虑为任何还没有主键的表添加一个主键。根据表的最大投影大小,使用最小的实际数值类型。这可以使每一行稍微紧凑一些,对于大型表可以节省大量空间。如果表有辅助索引,那么节省的空间会成倍增加,因为主键值在每个辅助索引项中都是重复的。除了减少磁盘上的数据大小,小主键还可以让缓冲池容纳更多的数据,加快各种操作并提高并发性。

如果表中某些较长的列已经有了主键,比如VARCHAR,可以考虑添加一个新的unsigned AUTO_INCREMENT列,并切换主键到该列,即使该列在查询中没有被引用。这种设计改变可以在二级索引中节省大量空间。用户可以将之前的主键列指定为UNIQUE NOT NULL,以实施与主键子句相同的约束,即防止所有这些列中出现重复值或NULL值。

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

应用程序性能考虑
InnoDB的可靠性和可扩展性比等价的MyISAM表需要更多的磁盘存储空间。您可以稍微改变列和索引的定义,以便更好地利用空间,减少处理结果集时的I/O和内存消耗,以及更好地利用索引查找的查询优化计划。

如果你设置了一个数字ID作为主键,使用这个值与其他表中的相关值进行交叉引用,尤其是在连接查询中。例如,与其接受一个国家名作为输入并进行查询以搜索相同的名称,不如进行一次查找以确定国家ID,然后进行其他查询(或单个连接查询)以跨几个表查找相关信息。与其将顾客或商品编号存储为数字字符串(可能会占用几个字节),不如将其转换为数字ID以便存储和查询。一个4字节的unsigned INT列可以索引超过40亿个元素。

发表评论

电子邮件地址不会被公开。