MySQL对于alter table查询来说非常慢

Dav*_*rks 50 mysql sql alter-table

为什么只需更新此表以添加列需要一个多小时?该表有15M行.它有2个索引和一个主键.ALTER TABLE查询现在处于"复制到tmp表"状态1小时15分钟.

ALTER TABLE `frugg`.`item_catalog_map` 
ADD COLUMN `conversion_url` TEXT NULL DEFAULT NULL
Run Code Online (Sandbox Code Playgroud)

表:

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      |         |
+------------------+------------+----------------------+--------------+------------------------+-----------+-------------+----------+--------+------+------------+---------+
Run Code Online (Sandbox Code Playgroud)

Ern*_*oso 61

MySQL的ALTER TABLE性能可能会成为非常大的表的问题.MySQL通过创建具有所需新结构的空表,将旧表中的所有数据插入新表并删除旧表来执行大多数更改.这可能需要很长时间,特别是如果您的内存不足并且表很大并且有很多索引.许多人都有使用ALTER TABLE操作的经验,这些操作需要数小时或数天才能完成.

无论如何,如果您需要继续使用alter table,可能以下资源可以帮助您:

  • 如果你的存储系统出现问题需要很长时间,那就不是MySQL了. (4认同)
  • @AndreKR-不,我认为这显然是MySQL问题。在包含900MB数据的表中添加一列(大约2.5万行包含较小的Blob)应该不会花费超过3个小时的时间,这就是我现在的位置。我的存储系统也没有错;数据库存储在相当不错的SSD上,我有很多可用的RAM用于缓存。我认为InnoDB在这种操作过程中只会吸收索引。我猜它没有优化的大容量插入机制,因此是一次一次重建新索引。 (3认同)
  • 我从varchar更改为30行表中的文本,一直在等待20分钟,并且仍在继续。会是什么呢? (2认同)
  • @朱尔斯你可能是对的。我想当我写评论时,我假设它是一个 MyISAM 表,因为我认为 InnoDB 不会发生“复制到 tmp 表”,但实际上问题中没有给出表类型。对于 InnoDB,计划几天的 ALTER TABLE 操作是很正常的,这就是像 https://github.com/facebookincubator/OnlineSchemaChange 这样的软件存在的原因。 (2认同)

小智 24

如果您不关心停机时间,我的建议是使用三个单独的ALTER TABLE语句.第一个语句删除所有现有的二级索引.第二个语句应用所有与列相关的更改.最后一个语句将删除的二级索引添加回来并应用其他索引更改.

另外两个提示:

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

    SET unique_checks=0;
    SET foreign_key_checks=0;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建多个二级索引时,将它们放在一个ALTER TABLE语句中而不是多个分隔的ALTER TABLE语句中.

下图显示了性能差异.方法1是你的方法,方法2是我的方式.方法2与50米工作台的方法1相比花费约3.47%的时间.该解决方案仅适用于MySQL(> = 5.5)InnoDB引擎.

在此输入图像描述


jdw*_*yah 9

Percona工具是大桌子的救星.

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

他们基本上:

  1. 创建重复的表
  2. 创建触发器以同步表
  3. 批量复制数据
  4. 校验
  5. 交换表

永远,但谁在乎,因为这意味着您可以在不停机的情况下更改列.

  • 如果您的表上有外键,则无济于事,因为您不能原子地重命名两个表 (2认同)

And*_*eKR 7

你的表有1500万行,这是一些东西.ALTER TABLE涉及复制表中的所有数据并重新创建索引.作为第一个测量,尝试在文件系统中复制数据文件(item_catalog_map.MYD,如果它是MyISAM),并查看需要多长时间.这是ALTER TABLE 至少要采用的时间.


gau*_*amc 6

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

  • 根据现有表创建一个新的空表,并更改此新的空表.
  • 执行大表的mysqldump,使其在大表中的每个记录都有一个完整的insert语句(开关-c和--skip-extended-insert)
  • 使用空重命名的large_table将此mysqldump导入另一个(空)数据库.
  • 从另一个数据库中获取此新重命名表的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;
    
    Run Code Online (Sandbox Code Playgroud)