如果您有 MyISAM
表格想要转换为 InnoDB
以获得更好的可靠性和可扩展性,请在转换前查看以下指南和提示。
在之前的 MySQL 版本中创建的分区 MyISAM
表格与 MySQL 8.3 不兼容。这些表格必须在升级前进行准备,或者删除分区,或者将其转换为 InnoDB
。请参阅 第 26.6.2 节,“与存储引擎相关的分区限制”,以获取更多信息。
当您从 MyISAM
表迁移到其他表时,降低 key_buffer_size
配置选项的值,以释放不再需要的缓存结果的内存。增加 innodb_buffer_pool_size
配置选项的值,该选项为 InnoDB
表分配缓存内存,类似于缓存结果。 InnoDB
的 缓冲池 缓存表数据和索引数据,提高查询速度,并将查询结果保留在内存中以便重用。有关缓冲池大小配置的指导,请参阅 第 10.12.3.1 节,“MySQL 如何使用内存”。
因为 MyISAM
表不支持 事务,您可能没有注意到 autocommit
配置选项和 COMMIT
和 ROLLBACK
语句。这些关键字对于允许多个会话并发读取和写入 InnoDB
表非常重要,提供了大量的可扩展性优势在写入密集型工作负载中。
当事务打开时,系统会保留事务开始时的数据快照,这可能会导致大量的开销,如果系统在事务运行时插入、更新和删除了数百万行数据。因此,请避免事务运行太长时间:
前面的提示可以节省内存和磁盘空间,这些空间可能在太长的事务中被浪费。当事务太短时,问题是过多的I/O。每次提交
,MySQL 都会确保每个更改都安全地记录到磁盘上,这涉及到一些I/O。
-
对于大多数
InnoDB
表操作,您应该使用设置autocommit=0
。从效率角度来看,这避免了在发出大量连续的INSERT
、UPDATE
或DELETE
语句时不必要的 I/O。从安全角度来看,这允许您发出ROLLBACK
语句,以恢复丢失或损坏的数据,如果您在 mysql 命令行或应用程序的异常处理程序中犯了错误。 -
autocommit=1
适用于InnoDB
表,当运行一系列查询以生成报告或分析统计数据时。在这种情况下,没有与COMMIT
或ROLLBACK
相关的 I/O Penalty,InnoDB
可以 自动优化只读工作负载。 -
如果您进行了一系列相关的更改,请在最后使用单个
COMMIT
来_finalize 所有更改。例如,如果您将相关的信息 piece 插入到多个表中,请在所有更改后执行单个COMMIT
。或者,如果您运行了许多连续的INSERT
语句,请在所有数据加载完成后执行单个COMMIT
;如果您正在执行数百万个INSERT
语句,可能需要每隔十万或一百万记录执行一个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
输出中看到最后一个警告。
有关更多信息,请参阅第17.7.5节,“InnoDB中的死锁”。
要从InnoDB
表中获得最佳性能,可以调整与存储布局相关的一些参数。
当你将大型、频繁访问的、包含重要数据的MyISAM
表转换为InnoDB
表时,调查和考虑innodb_file_per_table
和innodb_page_size
变量,以及ROW_FORMAT
和KEY_BLOCK_SIZE
子句。
在初始实验中,最重要的设置是innodb_file_per_table
。启用该设置时,默认情况下,新的InnoDB
表将隐式创建在每个表文件表空间中。与InnoDB
系统表空间相比,每个表文件表空间允许操作系统在表被截断或删除时回收磁盘空间。此外,每个表文件表空间还支持动态行格式和压缩行格式,以及相关功能,如表压缩、长变长列的高效离页存储和大索引前缀。有关更多信息,请参阅第17.6.3.2节,“每个表文件表空间”。
您也可以将 InnoDB
表存储在共享的通用表空间中,该表空间支持多个表和所有行格式。有关更多信息,请参阅 第 17.6.3.3 节,“通用表空间”。
您可能想创建一个 InnoDB
表,该表是 MyISAM 表的克隆,而不是使用 ALTER TABLE
进行转换,以便在转换前后同时测试旧表和新表。
创建一个空的 InnoDB
表,具有相同的列和索引定义。使用 SHOW CREATE TABLE
查看完整的 table_name
\GCREATE 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”。