【问题标题】:ALTER TABLE ADD COLUMN takes a long timeALTER TABLE ADD COLUMN 需要很长时间
【发布时间】:2011-11-27 19:25:55
【问题描述】:

我只是想在数据库中的表 (main_table) 中添加一个名为“位置”的列。我运行的命令是

ALTER TABLE main_table ADD COLUMN location varchar (256);

main_table 包含 > 2,000,000 行。它持续运行2个多小时,仍未完成。

我尝试使用mytop 监视此数据库的活动以确保查询没有被其他查询进程锁定,但似乎没有。应该需要那么长时间吗?实际上,我只是在运行此命令之前重新启动了机器。现在这个命令仍在运行。我不知道该怎么办。

【问题讨论】:

  • 这需要很长时间,因为索引和表中存在的行数。注意:varchar(255)
  • 我想你应该为它指定一个默认值。可能这就是它需要时间的原因吗?
  • 在这种情况下它默认为NULL,绝对不是为什么它需要很长时间。
  • MySQL 8.0.12 有一个ALTER TABLE .. ADD COLUMN .. ALGORITHM=INSTANT

标签: mysql performance alter


【解决方案1】:

您的ALTER TABLE 语句暗示mysql 将不得不重写表的每一行,包括新列。由于您有超过 200 万行,我肯定希望这需要大量时间,在此期间您的服务器可能主要受 IO 限制。您通常会发现执行以下操作会更高效:

CREATE TABLE main_table_new LIKE main_table;
ALTER TABLE main_table_new ADD COLUMN location VARCHAR(256);
INSERT INTO main_table_new SELECT *, NULL FROM main_table;
RENAME TABLE main_table TO main_table_old, main_table_new TO main_table;
DROP TABLE main_table_old;

通过这种方式,您可以在空表中添加列,并且基本上将数据写入该新表中,如果不锁定尽可能多的资源,您确定没有其他人会查看这些数据。

【讨论】:

  • @Malvolio 值得注意:我认为它是 MySQL 专有的 SQL 扩展......虽然不是 100% 确定。
  • 即使是,最后一次有人使用非 MySQL SQL RDBMS 是什么时候?朋友不要让朋友购买甲骨文。
  • 现在动态填写“fields_in_main_table”将是在脚本中使用的好东西。
  • 即使这种方法也必须重写整个表。我是否认为这种方法的唯一优点是它允许在添加新列期间使用当前表?
  • 如果您有引用表的外键,这对您没有帮助。
【解决方案2】:

我认为适当的答案是使用pt-online-schema-change 或gh-ost 之类的功能。

我们已经完成了超过 40 亿行的迁移,尽管它可能需要长达 10 天的时间,而且停机时间不到一分钟。

Percona 的工作方式与上述非常相似

  • 创建临时表
  • 在第一个表上创建触发器(用于插入、更新、删除),以便将它们复制到临时表中
  • 小批量迁移数据
  • 完成后,将表重命名为新表,然后删除另一个表

【讨论】:

  • 这种问题证明,较少投票或接受的解决方案有时是最好的或唯一正确的解决方案。有多少人投票给另一个不正确的解决方案 OMG。
  • @RaulR。这取决于用例。在我的用例中,另一个解决方案非常“正确”,并不是每个人都在 24/7 的生产环境中工作。
  • 请注意 pt-online-schema-change 实际上可能会导致阻塞表。如果您的大桌子有 fks 指向它,那么您可能会有阻塞时间或丢失 fks。您需要选择一个alter-foreign-keys-method,rebuild_constraints 将需要重新构建一个 fk,这将阻止引用表。
【解决方案3】:

Alter table 需要很长时间来处理像你这样的大数据,所以避免在这种情况下使用它,并使用一些像这样的代码:

select main_table.*, 
  cast(null as varchar(256)) as null_location, -- any column you want accepts null
  cast('' as varchar(256)) as not_null_location, --any column doesn't accept null
  cast(0 as int) as not_null_int, -- int column doesn't accept null
into new_table 
from main_table;

drop table main_table;
rename table new_table TO main_table;

【讨论】:

    【解决方案4】:

    您可以通过暂时关闭唯一检查和外键检查来加快处理速度。您还可以更改使用的算法。

    如果您希望新列位于表的末尾,那么您应该使用algorithm=instant:

    SET unique_checks = 0;
    SET foreign_key_checks = 0;
    ALTER TABLE main_table ADD location varchar(256), algorithm=instant;
    SET unique_checks = 1;
    SET foreign_key_checks = 1;
    

    否则,如果您需要列在特定位置,请使用algorithm=inplace:

    SET unique_checks = 0;
    SET foreign_key_checks = 0;
    ALTER TABLE main_table ADD location varchar(256) AFTER othercolumn, algorithm=inplace;
    SET unique_checks = 1;
    SET foreign_key_checks = 1;
    

    作为参考,我的 PC 使用就地算法更改了一个包含 2000 万行的表大约需要 2 分钟。如果您使用的是 Workbench 之类的程序,那么您可能希望在开始操作之前增加设置中的默认超时时间。

    如果您发现操作无限期挂起,那么您可能需要查看进程列表并杀死任何在表上锁定的进程。您可以使用以下命令来做到这一点:

    SHOW FULL PROCESSLIST;
    KILL PROCESS_NUMBER_GOES_HERE;
    

    【讨论】:

      【解决方案5】:

      DB2 z/OS 立即对列进行虚拟添加。并将表格置于咨询重组状态。在重组之前运行的任何东西都获得默认值,如果没有默认值,则为 null。更新完成后,它们会扩展更新的行。插入完成扩展。下一个重组扩展每个未扩展的行,并将默认值分配给它扩展的任何内容。

      只有真正的数据库才能很好地处理这个问题。 DB2 z/OS。

      【讨论】:

      • 您可能需要考虑,切换到不同的数据库将有其自身的影响,并且这个答案对近十年后的原始问题的提问者没有帮助。跨度>
      猜你喜欢
      • 2011-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 1970-01-01
      • 1970-01-01
      • 2015-09-05
      相关资源
      最近更新 更多