8

我有一个最大约 17 GB 的MySQL数据库,有 3800 万个条目。目前,我需要增加一列的大小(varchar 40 到 varchar 80)并添加更多列。

许多字段都已编入索引,包括我需要更改的字段。它是应用程序运行所必需的唯一对的一部分。在昨天尝试进行更改时,查询运行了将近四个小时而没有完成,当时我决定减少中断并恢复服务。

对这种大小的东西进行更改的最有效方法是什么?

其中许多条目也很旧,如果有一种很好的方法可以对条目进行分类,但仍然可以使用它们,这可能会通过使表的大小更易于管理来帮助解决这个问题。

4

3 回答 3

8

你有一些选择。

在任何情况下,您都应该在执行此操作之前进行备份。

一种可能性是让您的服务脱机并在原地执行,就像您尝试过的那样。如果你这样做,你应该禁用键检查和约束。

ALTER TABLE bigtable DISABLE KEYS;
SET FOREIGN_KEY_CHECKS=0;
ALTER TABLE (whatever);
ALTER TABLE (whatever else);
...
SET FOREIGN_KEY_CHECKS=1;
ALTER TABLE bigtable ENABLE KEYS;

这将允许 ALTER TABLE 操作进行得更快。当您执行 ENABLE KEYS 时,它将立即重新生成所有索引。

另一种可能性是使用您想要的新模式创建一个新表,然后禁用新表上的键,然后按照@Bader 的建议执行并插入旧表的内容。

建立新表后,您将重新启用其上的键,然后将旧表重命名为“old_bigtable”之类的名称,然后将新表重命名为“bigtable”。

您可以在填充新表时保持服务在线。但这可能效果不佳。

第三种可能性是将您的巨型表转储(到平面文件),然后将其加载到具有新布局的新表中。这与第二种可能性非常相似,只是您可以免费获得一个表备份。SELECT DATA INTO OUTFILE你可以用and让这个过程变得非常快LOAD DATA INFILE。您需要有权访问服务器计算机的文件系统才能执行此操作。

在所有情况下,禁用然后重新启用约束和键以使事情快速进行。

于 2012-10-19T16:46:53.247 回答
5

使用您想要的具有不同名称的新结构创建一个新表,例如 NewTable。

然后使用以下查询将旧表中的数据插入到这个新表中:

INSERT INTO NewTable (field1, field2, etc...) SELECT field1, field2, ... FROM OldTable

完成后,您可以删除旧表并将新表重命名为原始名称

DROP TABLE `OldTable`;
RENAME TABLE `NewTable` TO `OldTable` ;

我在一张非常大的桌子上尝试过这种方法,它比改变桌子要快得多。

于 2012-10-19T16:34:33.810 回答
2

在 MySQL 5.1 和 5.5 中,某些 alter 语句被增强,只修改结构而不重写整个表(http://dev.mysql.com/doc/refman/5.5/en/alter-table.html - 在-地方)。虽然这个可用性因您所做的更改类型和使用的引擎而异,但最大的价值来自 InnoDB 插件。在您进行特定更改的情况下,尽管整个表都将被重写。

当我们遇到这些问题时,我们通常会尝试利用副本数据库。只要您是在添加而不是删除,您就可以先对副本运行 DDL,然后安排短暂的中断以将副本提升为主角色。如果您碰巧在 RDS 上,这甚至是他们对副本实例的建议用途之一http://aws.amazon.com/about-aws/whats-new/2012/10/11/amazon-rds-mysql-rr -促销/

其他一些替代方案包括:

  • 将记录的子集选择到具有所需结构的新表中(用于INTO OUTFILE避免表锁定)。完成后,您可以安排维护时段和REPLACE INTO/或UPDATE自初始数据复制以来原始表中已更改的任何记录。更新完成后RENAME TABLE...,两个表中的一个将包含更改。
  • 使用 Percona 的 pt-online-schema-change 之类的工具:http ://www.percona.com/doc/percona-toolkit/2.1/pt-online-schema-change.html 。此工具适用于触发器,因此如果您想要更改的表上已有触发器,则可能不适合您的需求。
于 2012-10-19T18:16:05.360 回答