【问题标题】:Optimize mySql for faster alter table add column优化 mySql 以更快地改变表添加列
【发布时间】:2023-03-05 13:25:01
【问题描述】:

我有一个包含 170,002,225 行、大约 35 列和两个索引的表。我想添加一列。 alter table 命令用了大约 10 个小时。在那段时间处理器似乎既不忙,也没有过多的 IO 等待。这是一个具有大量内存的 4 路高性能盒子。

这是我能做到的最好的吗?有什么我可以在调整 db 时优化添加列的方法吗?

【问题讨论】:

  • HDD 系统是导致速度变慢的原因,您所能做的就是获得更快的 HDD 系统(如果您能负担得起 FusionIO),或者您只需要等待。或者,不要添加列:)
  • 你使用什么 MySQL 版本?
  • @Michael 他说“也没有过多的 io 等待。”
  • @FractalizeR - 由于没有说明如何获取 IO 信息,我倾向于认为是内存/硬盘子系统(或 raid 控制器,如果有的话)阻止了桌子。根据我的经验(我使用大型数据集),当涉及到诸如此类的性能问题时,几乎总是应该归咎于存储系统。
  • @Michael,是的,就是这样。但无论如何,在你可以肯定地说出什么之前,需要进行彻底的调查。

标签: mysql performance alter


【解决方案1】:

我过去遇到过非常相似的情况,我通过这种方式提高了操作的性能:

  1. 创建一个包含新列的新表(使用当前表的结构)。
  2. 执行INSERT INTO new_table (column1,..columnN) SELECT (column1,..columnN) FROM current_table;
  3. 重命名当前表
  4. 使用当前表的名称重命名新表。

【讨论】:

  • 一些注意事项... 1. CREATE TABLE new_table LIKE table; 2. INSERT INTO new_table SELECT * FROM table; 3&4.如果将第 3 步和第 4 步合并到此,您可以获得伪原子重命名:RENAME TABLE table = old_table, table = new_table;
  • 这也是我使用的程序。通常,我们会编写 select into 脚本以减慢速度(滴灌),以允许正常的数据库操作继续正常进行。
  • 如何处理在复制旧表时(即重命名之前)发生在旧表上的写入?
  • 针对这种繁重的操作,我们将网站置于维护模式。在迁移期间阻止 IO 也将提高性能。
  • 在这种情况下你如何处理 FK(InnoDB)?
【解决方案2】:

MySQL 中的ALTER TABLE 实际上是要创建一个具有新模式的新表,然后重新INSERT 所有数据并删除旧表。您可以通过创建新表、加载数据然后重命名表来节省一些时间。

来自“高性能 MySQL 书”(percona 家伙):

有效加载 MyISAM 表的常用技巧是禁用键、加载数据并重新设置键:

mysql> ALTER TABLE test.load_data DISABLE KEYS;
-- load data
mysql> ALTER TABLE test.load_data ENABLE KEYS;

【讨论】:

    【解决方案3】:

    好吧,我建议使用最新的 Percona MySQL 版本,因为 MySQL 手册中有以下注释

    在其他情况下,MySQL 创建一个 临时表,即使数据 严格来说不需要复制。 对于 MyISAM 表,您可以加快 索引重新创建操作(其中 是变化最慢的部分 过程)通过设置 myisam_sort_buffer_size 系统 可变为高值。

    您可以先执行ALTER TABLE DISABLE KEYS,然后添加列,然后添加ALTER TABLE ENABLE KEYS。我看这里什么都做不了。

    顺便说一句,你不能去 MongoDB 吗?添加列时它不会重建任何内容。

    【讨论】:

    • 禁用键和启用键只适用于 myisam 引擎,有 170,002,225 行,我认为它使用的是 innodb!
    • @Neo MyISAM 的最大行数约为 2^32,远远超过 1.7 亿。使用 --with-big-tables 选项,这个限制提高到 ~2^64,甚至更多。
    • MyISAM 表由于读锁定而存在并发问题。 InnoDB 显然是要走的路。
    • @FoneyOp 我怀疑切换到 InnoDB 会加速 ALTER TABLE
    • 同意。由于此处提到的原因,对于 ALTER TABLE,InnoDB 几乎总是较慢。该评论是指 MyISM 最大行数在 170M 行内。
    【解决方案4】:

    也许您可以在更改表之前删除索引,因为构建索引需要花费大部分时间?

    【讨论】:

    • 索引要么不重建,要么只重建一次,具体取决于ALTER TABLE。删除它并重新添加它不会节省任何时间。
    • 我指的是二进制搜索,构建索引的成本很高。我从来没有在 1700 亿行上尝试过。
    • 只有 1.7 亿行,而不是数十亿行;)
    • @Phpdna,当建立索引时,它会进行排序,即 O(nlog n),而不是二分查找,即 O(log n)。
    • 这个答案是最合适的,在实践中,Ive tested on - table that has >22.000.000 rows. What I did - dropped all indexes (there were 3), then added the field (that takes 4.23 on HDD) and recreate indexes, which took less then 2 mins each. Darius Jahandarie - isn't right, he do not know, in practice what he is talking about, the answer of RRUZ also seems to work but in practice - INSERT INTO ... SELECT * FROM` 需要几个小时,而删除索引-添加列-重新创建索引似乎真的很快的决定。 PS我宁愿先锁定这张表,以防止查询变慢。
    【解决方案5】:

    结合其他答案中的一些 cmets,这是对我有用的解决方案(MySQL 5.6):

    1. create table mytablenew like mytable;
    2. alter table mytablenew add column col4a varchar(12) not null after col4;
    3. alter table mytablenew drop index index1, drop index index2,...drop index indexN;
    4. insert into mytablenew (col1,col2,...colN) select col1,col2,...colN from mytable;
    5. alter table mytablenew add index index1 (col1), add index index2 (col2),...add index indexN (colN);
    6. rename table mytable to mytableold, mytablenew to mytable

    在一个 75M 行的表上,在插入之前删除索引会导致查询在 24 分钟内完成,而不是 43 分钟。

    其他答案/cmets 有 insert into mytablenew (col1) select (col1) from mytable,但如果您在选择查询中有括号,这将导致 ERROR 1241 (21000): Operand should contain 1 column(s)

    其他答案/cmets 有 insert into mytablenew select * from mytable;,但如果您已经添加了一列,这会导致 ERROR 1136 (21S01): Column count doesn't match value count at row 1

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-10
      • 1970-01-01
      • 2016-03-18
      • 2023-03-15
      • 1970-01-01
      • 2021-11-10
      • 2011-05-18
      相关资源
      最近更新 更多