在大表中添加列的MySQL性能

24

我在Ubuntu 13.10上使用apt-get本地安装了MySQL 5.5.37和InnoDB。我的桌面电脑配置是i7-3770 + 32GB内存 + SSD硬盘。对于一个包含150万条记录的“mytable”表,以下DDL查询需要超过20分钟(!):

ALTER TABLE mytable ADD some_column CHAR(1) NOT NULL DEFAULT 'N';

有办法改进吗? 我已经检查过了。

show processlist;

它显示正在复制我的表格,这令人不安地不方便。有没有办法关闭这个复制?添加列以改进大型表的性能还有其他方法吗?

除此之外,我的数据库相对较小,只有1.3GB的转储大小。因此,理论上应该可以完全放入内存。

有没有可以帮助的设置?迁移到Precona会对我产生任何影响吗?

补充:

innodb_buffer_pool_size = 134217728

3
尽管它适合内存,但它必须将所有内容写回磁盘以使其永久。 - Barmar
可能是优化mySql以加快alter table add column速度的重复问题。 - cs_alumnus
1
他们正在讨论对我来说MyISAM和InnoDB的区别。 - Artem
5个回答

40

有没有其他方法可以提高向大表中添加列的性能?

简短回答:没有。您可以即时添加 ENUM 和 SET 值,并且在仅对写操作进行锁定时添加二级索引,但更改表结构始终需要复制表。

长回答:您真正的问题不是性能,而是锁定时间。它不重要是否慢,重要的是其他客户端无法执行查询,直到 ALTER TABLE 完成。在这种情况下有一些选择:

  1. 您可以使用 Percona 工具包中的 pt-online-schema-change。首先备份您的数据!这是最简单的解决方案,但可能并不适用于所有情况。

  2. 如果您不使用外键,并且因为有很多索引而变慢,那么通过创建一个不包含二级索引但包含所需更改的表的副本,将其填充到数据中,并在最后使用单个 alter table 创建所有索引可能更快。

  3. 如果很容易为您创建副本,比如您托管在 Amazon RDS 上,则可以创建一个主-主副本,在那里运行 alter table,让它重新同步,然后在完成后切换实例。

更新

正如其他人提到的,MySQL 8.0 INNODB 添加了对即时添加列的支持。这不是一个神奇的解决方案,它有限制和副作用 -- 它只能成为最后一列,表必须没有全文本索引等 -- 但应该在许多情况下有所帮助。

您可以指定明确的 ALGORITHM=INSTANT LOCK=NONE 参数,如果无法进行即时模式更改,则 MySQL 将失败并显示错误,而不是回退到 INPLACECOPY。例如:

ALTER TABLE mytable
ADD COLUMN mycolumn varchar(36) DEFAULT NULL,
ALGORITHM=INPLACE, LOCK=NONE;

MySQL 8.0 InnoDB现在支持即时添加列


1
你可以使用Percona toolkit中的[pt-online-schema-change][1]。在操作前备份数据!这是最简单的解决方案,但并不适用于所有情况。 -- 这是非常好的建议。 - Evan Volgas

6

截至2018年5月8日,MySQL 8现在支持它。请参考链接:https://mysqlserverteam.com/mysql-8-0-innodb-now-supports-instant-add-column/ - asgs

6

我知道这是一个比较老的问题,但今天我遇到了类似的问题。我决定创建一个新表格,并将旧表格导入新表格中。就像这样:

CREATE TABLE New_mytable  LIKE mytable ;

ALTER TABLE New_mytable  ADD some_column CHAR(1) NOT NULL DEFAULT 'N';

insert into New_mytable  select * from mytable ;

那么

START TRANSACTION;
insert into New_mytable  select * from mytable where id > (Select max(id) from New_mytable) ;

RENAME TABLE mytable TO Old_mytable;

RENAME TABLE New_mytable TO mytable;
COMMIT;

这并不会使更新过程更快,但可以将停机时间最小化。
希望能对您有所帮助。

非常感谢。但这是最好的方法吗?我正在尝试其他解决方案,但仍未找到。 :) - Tien Nguyen
1
这是向大表添加列并重建表的最快方法,几乎没有停机时间。如果您正在运行MySQL 8.0或更高版本,则可以尝试ColinM的解决方案。 - Nebu
在使用这种方法时需要记住的一点是,在编写此评论时,CREATE TABLE ... LIKE语句不会创建表的完全副本。例如,外键配置不会复制到新表中。来自文档:https://dev.mysql.com/doc/refman/5.7/en/create-table-like.html - cpr4t3s
3
如果原始表格不仅仅是插入数据,还进行了更新或删除操作,会怎么样? - Julio

2

很酷的功能,但我担心它会使alter table变得更慢。 - Artem

1
无法避免在添加或删除列时复制表格,因为结构会发生变化。您可以在不进行表格复制的情况下添加或删除辅助索引。
您的表格数据不驻留在内存中。索引可以驻留在内存中。
150万条记录并不是很多,20分钟似乎很长,但也许您的行很大并且有很多索引。
在复制表格时,您仍然可以从表格中选择行。但是,如果您尝试进行任何更新,则将被阻止直到ALTER完成。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接