MySQL - 为 InnoDB 更改表的最快方法

Ran*_*Ran 15 mysql innodb alter-table ddl

我有一个要更改的 InnoDB 表。该表有大约 80M 行,并退出了一些索引。

我想更改其中一列的名称并添加更多索引。

  • 最快的方法是什么(假设我什至可能遭受停机时间 - 服务器是未使用的从站)?
  • 是“简单的” alter table,最快的解决方案吗?

这个时候,我只关心速度 :)

Rol*_*DBA 16

加速 ALTER TABLE 的一种可靠方法是删除不必要的索引

以下是加载新版本表的初始步骤

CREATE TABLE s_relations_new LIKE s_relations;
#
# Drop Duplicate Indexes
#
ALTER TABLE s_relations_new
    DROP INDEX source_persona_index,
    DROP INDEX target_persona_index,
    DROP INDEX target_persona_relation_type_index
;
Run Code Online (Sandbox Code Playgroud)

请注意以下事项:

  • 我删除了 source_persona_index 因为它是其他 4 个索引中的第一列

    • unique_target_persona
    • unique_target_object
    • source_and_target_object_index
    • source_target_persona_index
  • 我删除了 target_persona_index 因为它是其他 2 个索引中的第一列

    • target_persona_relation_type_index
    • target_persona_relation_type_message_id_index
  • 我删除了 target_persona_relation_type_index 因为前 2 列也在 target_persona_relation_type_message_id_index 中

OK 这会处理不必要的索引。是否存在基数较低的索引?这是确定的方法:

运行以下查询:

SELECT COUNT(DISTINCT sent_at)               FROM s_relations;
SELECT COUNT(DISTINCT message_id)            FROM s_relations;
SELECT COUNT(DISTINCT target_object_id)      FROM s_relations;
Run Code Online (Sandbox Code Playgroud)

根据您的问题,大约有 80,000,000 行。根据经验,如果所选列的基数大于表行数的 5%,MySQL 查询优化器将不使用索引。在这种情况下,这将是 4,000,000。

  • 如果COUNT(DISTINCT sent_at)> 4,000,000
    • 然后 ALTER TABLE s_relations_new DROP INDEX sent_at_index;
  • 如果COUNT(DISTINCT message_id)> 4,000,000
    • 然后 ALTER TABLE s_relations_new DROP INDEX message_id_index;
  • 如果COUNT(DISTINCT target_object_id)> 4,000,000
    • 然后 ALTER TABLE s_relations_new DROP INDEX target_object_index;

一旦确定了这些索引的有用或无用,您就可以重新加载数据

#
# Change the Column Name
# Load the Table
#
ALTER TABLE s_relations_new CHANGE sent_at sent_at_new int(11) DEFAULT NULL;
INSERT INTO s_relations_new SELECT * FROM s_relations;
Run Code Online (Sandbox Code Playgroud)

就是这样,对吗?不 !!!

如果您的网站一直在运行,那么在加载 s_relations_new 期间可能会有针对 s_relations 运行的 INSERT。你怎么能找回那些丢失的行?

找到 s_relations_new 中的最大 id,并从 s_relations 中添加该 ID 之后的所有内容。为确保该表被冻结并仅用于此更新,您必须有一点停机时间以获取最后插入到 s_relation_new 中的行。这是你要做的:

在操作系统中,重新启动 mysql,以便除了 root@localhost(禁用 TCP/IP)之外没有其他人可以登录:

$ service mysql restart --skip-networking
Run Code Online (Sandbox Code Playgroud)

接下来,登录到 mysql 并加载最后几行:

mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> ALTER TABLE s_relations_new RENAME s_relations;
Run Code Online (Sandbox Code Playgroud)

然后,正常重启mysql

$ service mysql restart
Run Code Online (Sandbox Code Playgroud)

现在,如果您不能关闭 mysql,您将不得不对 s_relations 进行诱饵和切换。只需登录到 mysql 并执行以下操作:

mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations_old WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations_new RENAME s_relations;
Run Code Online (Sandbox Code Playgroud)

试一试 !!!

CAVEAT :一旦您对此操作感到满意,您可以尽早删除旧表:

mysql> DROP TABLE s_relations_old;
Run Code Online (Sandbox Code Playgroud)


mez*_*zis 12

正确答案取决于您使用的 MySQL 引擎的版本。

如果使用 5.6+,重命名和添加/删除索引在线执行,即不复制所有表的数据。

ALTER TABLE像往常一样使用,重命名和索引删除几乎是即时的,并且索引添加相当快(就像一次读取所有表一样快)。

如果使用 5.1+,并且启用了 InnoDB 插件,添加/删除索引也将在线。不确定重命名。

如果使用旧版本,ALTER TABLE仍然是最快的——但可能会非常慢,因为你的所有数据都将重新插入到后台的临时表中。

最后,是时候揭穿神话了。不幸的是,我在这里没有足够的业力来评论答案,但我觉得纠正投票最多的答案很重要。这是错误的

根据经验,如果所选列的基数大于表行数的 5%,MySQL 查询优化器将不使用索引

它实际上是相反的

索引对于选择行很有用,因此它们具有基数很重要,这意味着许多不同的值和具有相同值的统计数据很少的行。

  • 在 MySQL 5.5 上,我发现“RENAME TABLE”即时(正如预期的那样),但是重命名主键的“CHANGE COLUMN”做了一个完整的复制...... 7 小时!可能只是因为它是主键?不好。 (3认同)