如何在MySQL中将列添加到大表


13

我是PHP开发人员,所以不要严格。我有一张大桌子〜5.5gb转储。我们的项目经理决定在其中添加新列以执行新功能。表是InnoDB,所以我尝试了什么:

  1. 使用表锁更改屏幕中的表。花了大约30个小时,什么也没有。所以我就停止了。首先,我犯了一个错误,因为我没有结束所有事务,但是第二次不是多重锁。状态为copy to tmp table

  2. 由于我还需要对此表进行分区,因此我们决定进行转储,重命名并使用相同的名称和新的结构来创建表。但是转储正在严格复制(至少我没有发现其他东西)。所以我添加了一个新的转储列sed并进行查询。但是一些奇怪的错误开始了。我相信这是由字符集引起的。utf-8中的表和文件在之后成为us-ascii sed。所以我在30%的数据上遇到了错误(未知命令'\'')。所以这也是一个坏方法。

还有什么其他方法可以完成此操作并提高性能(我可以使用php脚本来完成,但是要花很多时间)。INSERT SELECT在这种情况下的性能如何。

感谢您的任何进步。

Answers:


12

使用MySQL Workbench。您可以右键单击表,然后选择“发送到SQL编辑器”->“创建语句”。这样,不会遗忘添加任何表“属性”(包括CHARSETCOLLATE)。
对于海量数据,我建议您清理表或您使用的数据结构(一个好的DBA会很方便)。如果不可能:

  • 重命名表(ALTER),并使用CREATE从Workbench获得的脚本创建一个新表。您还可以使用所需的新字段扩展该查询
  • 将旧表中的数据批量加载到新表中:
    SET FOREIGN_KEY_CHECKS = 0;
    SET UNIQUE_CHECKS = 0;
    SET AUTOCOMMIT = 0;
    INSERT INTO new_table (fieldA, fieldB, fieldC, ..., fieldN)
       SELECT fieldA, fieldB, fieldC, ..., fieldN
       FROM old_table
    SET UNIQUE_CHECKS = 1;
    SET FOREIGN_KEY_CHECKS = 1;
    COMMIT;

    这样,您可以避免索引/ etc逐条记录运行。对表的“更新”仍然会很慢(因为数据量很大),但这是我能想到的最快的方法。

    编辑:阅读文章,以获取有关在上面的示例查询中使用的命令的详细信息;)

我的选择很好。我得到了SET NAMES utf8COLLATION。但MEH IDK为什么数据的30%后损坏sed。我认为批量加载将是最快的,但也许我缺少更多的东西。谢谢马克
ineersa

1
@ineersa数据损坏可能有很多原因:例如,您使用不支持所有字符的编辑器打开了文件并保存了它。或者,您尝试从转储导入的方式损坏了数据(这是错误的,无法正确读取文件)。或者,同一个人可能将某些数据的一部分标识为表达式(例如,“ james \ robin” ==“ \ r”作为表达式)或命令等。这就是为什么我不建议使用转储,甚至不使用二进制数据转储工具的原因仅适用于dev.mysql.com/doc/refman/5.6/en/mysqldump.html(对于MS SQL Server则为BCP)。它出错太多次了……

是的,我尝试使用十六进制。它没有帮助。您也可以在使用sed mysql之后以某些名称(并非全部)将\'标识为命令。多数民众赞成在奇怪和越野车。今晚将尝试批量加载。希望至少在10到15小时内完成。
ineersa 2013年

@ineersa希望会。您也可以尝试仅添加部分数据,例如将其添加10%,以查看所需的时间-并对整个交易进行估算。不过,这将是一个非常粗略的估计,如果缓存/内存/任何内容被填满/过载,事情都会变慢。

1
谢谢马克。工作很棒。然后从转储中恢复甚至更快。花了大约5个小时。
ineersa 2013年

5

您的sed想法是一种不错的方法,但是如果没有错误或您运行的命令,我们将无法为您提供帮助。

但是,一种在线更改大型表的众所周知的方法是pt-online-schema-change。从文档中复制了此工具的简单忽略:

pt-online-schema-change的工作原理是创建要更改的表的空副本,根据需要对其进行修改,然后将行从原始表复制到新表中。复制完成后,它将移走原始表,并用新表替换它。默认情况下,它也会删除原始表。

此方法可能还需要一段时间才能完成,但是在此过程中,原始表将完全可用。


我将在今晚晚些时候尝试批量加载。如果无法正常工作,则可能需要此工具。使用sed作为命令后无法识别某些符号会导致错误。例如'D\'agostini'会导致错误unknown command '\''。但并非总是如此,就像30%的情况一样。多数民众赞成在奇怪和越野车。即使是十六进制转储也是如此。谢谢Derek。
ineersa 2013年

4

alter table add column, algorithm=inplace, lock=none 将更改MySQL 5.6表,而不会复制该表,也不会影响锁定。

昨天刚刚进行了此测试,批量将70K行插入到280K第7行分区表中,将10K行插入每个分区,其间有5秒钟的睡眠时间以允许其他吞吐量。

开始大量插入,然后在单独的会话中alter在MySQL Workbench中启动上述在线语句,在alter插入之前完成操作,添加了两个新列,并且更改未产生任何行,这意味着MySQL不复制任何行。


1
为什么这个答案没有获得更多的选票?,它行不通吗?
fguillen

1

当前,更改大型表的最佳选择可能是https://github.com/github/gh-ost

gh-ost是用于MySQL的无触发在线模式迁移解决方案。它是可测试的,并提供暂停,动态控制/重新配置,审计和许多操作特权。

gh-ost在整个迁移过程中都会在主服务器上产生少量工作负载,与已迁移表上的现有工作负载分离。

它是基于多年使用现有解决方案的经验而设计的,并且改变了表迁移的范例。


1

我认为Mydumper / Myloader是执行以下操作的好工具:每天都在进步。您可以利用您的CPU并可以并行加载数据:http : //www.percona.com/blog/2014/03/10/new-mydumper-0-6-1-release-offers-several-performance-and-可用性功能/

我已经在几个小时内加载了数百GB的MySQL表。

现在,当涉及到添加新列时,这很棘手,因为MySQL将整个表复制到内存TMP区域,ALTER TABLE...尽管MySQL 5.6表示它可以进行在线模式更改,但是对于没有锁的海量表,我还没有设法在线进行更改。争论至今。


-2

我只是有同样的问题。一些解决方法:

创建表new_table SELECT * FROM oldtable;

从new_table删除

ALTER TABLE new_table添加列new_column int(11);

INSERT INTO new_table选择*,old_table为0

删除表old_table; 重命名表new_table TO old_table;


为什么不只是在create table语句中添加where子句,这样它就不会选择任何数据?截断表也比删除数据更有效
Joe W

为什么要删除,何时必须稍后再插入。可以在ADD COLUMN本身定义default = 0。
user195280 '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.