MySQL 对于更改表查询非常慢

问题描述 投票:0回答:8

为什么仅仅更新此表以添加列就需要一个多小时?该表有 15M 行。它有 2 个索引和一个单键主键。 ALTER TABLE 查询现在已处于“复制到临时表”状态 1 小时 15 分钟。

ALTER TABLE `frugg`.`item_catalog_map` 
ADD COLUMN `conversion_url` TEXT NULL DEFAULT NULL

表:

mysql> describe item_catalog_map;
+------------------------+---------------+------+-----+---------+-------+
| Field                  | Type          | Null | Key | Default | Extra |
+------------------------+---------------+------+-----+---------+-------+
| catalog_unique_item_id | varchar(255)  | NO   | PRI | NULL    |       |
| catalog_id             | int(11)       | YES  | MUL | NULL    |       |
| item_id                | int(11)       | YES  | MUL | NULL    |       |
| price                  | decimal(10,2) | YES  |     | 0.00    |       |
+------------------------+---------------+------+-----+---------+-------+

mysql> show index from item_catalog_map;
+------------------+------------+----------------------+--------------+------------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table            | Non_unique | Key_name             | Seq_in_index | Column_name            | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+------------------+------------+----------------------+--------------+------------------------+-----------+-------------+----------+--------+------+------------+---------+
| item_catalog_map |          0 | PRIMARY              |            1 | catalog_unique_item_id | A         |    15485115 |     NULL | NULL   |      | BTREE      |         |
| item_catalog_map |          1 | IDX_ACD6184FCC3C66FC |            1 | catalog_id             | A         |          18 |     NULL | NULL   | YES  | BTREE      |         |
| item_catalog_map |          1 | IDX_ACD6184F126F525E |            1 | item_id                | A         |    15485115 |     NULL | NULL   | YES  | BTREE      |         |
+------------------+------------+----------------------+--------------+------------------------+-----------+-------------+----------+--------+------+------------+---------+
mysql sql alter-table
8个回答
82
投票

对于非常大的表,MySQL 的 ALTER TABLE 性能可能会成为问题。 MySQL执行 大多数更改是通过创建一个具有所需新结构的空表、将旧表中的所有数据插入新表以及删除旧表来实现的。这可能需要很长时间,特别是当您内存不足且表很大且有大量索引时。许多人都有过需要数小时或数天才能完成的 ALTER TABLE 操作的经验。

无论如何,如果您需要继续更改表,也许以下资源可以帮助您:


39
投票

如果您不关心停机时间,我的建议是使用三个单独的

ALTER TABLE
语句。第一条语句删除所有现有的二级索引。第二条语句应用所有与列相关的更改。最后一条语句将删除的二级索引添加回来并应用其他索引更改。

另外两个提示:

  1. 在应用索引更改之前,执行以下两条语句,并在完成索引更改后将值更改回1。

    SET unique_checks=0;
    SET foreign_key_checks=0;
    
  2. 创建多个二级索引时,请将它们放在一个

    ALTER TABLE
    语句中,而不是多个单独的
    ALTER TABLE
    语句。

下图展示了性能差异。方法1是你的方法,方法2是我的方法。 对于 50m 的表,与方法 1 相比,方法 2 大约需要 3.47% 的时间。 该解决方案仅适用于 MySQL(>=5.5)InnoDB 引擎。

enter image description here


11
投票

为了最大限度地减少我想要更改的大表的锁定,我执行以下操作:

  • 根据现有表创建一个新的空表,并更改这个新的空表。
  • 对大表执行 mysqldump,使其在大表中的每条记录都有一个完整的插入语句(开关 -c 和 --skip-extended-insert)
  • 将此 mysqldump 导入到另一个(空)数据库中,并将空重命名为 large_table。
  • 从其他数据库获取这个新重命名表的 mysqldump,并将其导入到原始数据库中
  • 将原数据库中的large_table和large_table_new重命名。

    mysql> create table DATABASE_NAME.LARGE_TABLE_NEW like DATABASE_NAME.LARGE_TABLE;
    mysql> alter table DATABASE_NAME.LARGE_TABLE_NEW add column NEW_COLUMN_NAME COL_DATA_TYPE(SIZE) default null;
    
    $ mysqldump -c --no-create-info --skip-extended-insert --no-create-db -u root -p DATABASE_NAME LARGE_TABLE > LARGE_TABLE.sql
    
    mysql> create table test.LARGE_TABLE like DATABASE_NAME.LARGE_TABLE;
    
    $ mysql -u root -p -D test < LARGE_TABLE.sql
    
    mysql> rename table test.LARGE_TABLE to test.LARGE_TABLE_NEW;
    
    $ mysqldump -c --no-create-info --skip-extended-insert --no-create-db -u root -p test LARGE_TABLE_NEW > LARGE_TABLE_NEW.sql
    
    $ mysql -u root -p -D DATABASE_NAME < LARGE_TABLE_NEW.sql
    
    mysql> rename table DATABASE_NAME.LARGE_TABLE to DATABASE_NAME.LARGE_TABLE_OLD, DATABASE_NAME.LARGE_TABLE_NEW to DATABASE_NAME.LARGE_TABLE;
    

10
投票

Percona 工具是这些带有大桌子的东西的救星。

http://www.percona.com/doc/percona-toolkit/2.1/pt-online-schema-change.html

他们基本上:

  1. 创建重复表
  2. 创建触发器来同步表
  3. 批量复制数据
  4. 验证
  5. 交换桌子

需要很长时间,但谁在乎呢,因为这意味着您可以在不停机的情况下更换色谱柱。


8
投票

你的表有 1500 万行,这很了不起。 ALTER TABLE 涉及复制表中的所有数据并重新创建索引。作为第一个测量,请尝试将数据文件(如果是 MyISAM,则为 item_catalog_map.MYD)复制到文件系统中,并查看需要多长时间。这是 ALTER TABLE 至少花费的时间。


3
投票

我遇到了同样的问题,我只是使用以下命令重新启动了 MySQL 服务:

sudo service mysql restart

在 Ubuntu 中,此后

ALTER TABLE
命令立即执行。


0
投票

我遇到了类似的问题,对于在 MariaDB Docker 容器内运行的包含 1100 万行的表来说,

ALTER TABLE
的速度非常慢。同样的
ALTER TABLE
在同样具有 1100 万行的 RDS 实例上也快如闪电。

我正在运行 Mac OS 12.4,Docker 容器的数据位于文件系统卷上。正如其他答案所指出的,瓶颈是临时表的创建。

修复方法是在 Docker 实验设置中启用 VirtioFS。

ALTER TABLE
然后花费了大约 10 分钟,而不是可能需要 8 小时。


0
投票

我通过以下步骤解决了问题:

  • 暂时删除了常规检查。

    设置 unique_checks=0; 设置foreign_key_checks = 0;

  • 增加了此帖子之后的资源。 从约 9 小时减少到约 37 秒

© www.soinside.com 2019 - 2024. All rights reserved.