MySQL对于alter表查询非常慢

MySQL对于alter表查询非常慢,mysql,sql,alter-table,Mysql,Sql,Alter Table,为什么仅仅更新此表以添加列就要花费一个多小时?这张桌子有15米的行。它有两个索引和一个单键主键。ALTER TABLE查询已处于“复制到tmp TABLE”状态1小时15分钟 ALTER TABLE `frugg`.`item_catalog_map` ADD COLUMN `conversion_url` TEXT NULL DEFAULT NULL 表: mysql> describe item_catalog_map; +------------------------+----

为什么仅仅更新此表以添加列就要花费一个多小时?这张桌子有15米的行。它有两个索引和一个单键主键。ALTER TABLE查询已处于“复制到tmp 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      |         |
+------------------+------------+----------------------+--------------+------------------------+-----------+-------------+----------+--------+------+------------+---------+

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

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

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


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

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

    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;
    
    mysql>创建表DATABASE\u NAME.LARGE\u table\u新建类似DATABASE\u NAME.LARGE\u table;
    mysql>alter table DATABASE\u NAME.LARGE\u table\u NEW add column\u NAME COL\u DATA\u TYPE(SIZE)默认为空;
    $mysqldump-c--无创建信息--跳过扩展插入--无创建db-u root-p数据库\u NAME LARGE\u TABLE>LARGE\u TABLE.sql
    mysql>创建表test.LARGE\u表,如数据库\u NAME.LARGE\u表;
    $mysql-u root-p-D test将table test.LARGE\u table重命名为test.LARGE\u table\u NEW;
    $mysqldump-c--无创建信息--跳过扩展插入--无创建db-u root-p测试大表格\u NEW>LARGE\u TABLE\u NEW.sql
    $mysql-u root-p-D数据库\u NAME将表DATABASE_NAME.LARGE_table重命名为DATABASE_NAME.LARGE_table_OLD,DATABASE_NAME.LARGE_table_NEW重命名为DATABASE_NAME.LARGE_table;
    

Percona工具是这种东西的救命稻草,有大桌子

他们基本上:

  • 创建重复表
  • 创建同步表的触发器
  • 大容量复制数据
  • 核实
  • 交换表

  • 这需要花费很长时间,但谁在乎呢,因为这意味着您可以在不停机的情况下更改列。

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

    另外两个小贴士:

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

    SET unique_checks=0;
    SET foreign_key_checks=0;
    
  • 创建多个二级索引时,将它们放在一个
    altertable
    语句中,而不是放在多个分隔的
    altertable
    语句中

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


    可能这个有类似问题的线程会帮你解决问题…也可以浏览这个线程…如果有某种进度条、状态消息或至少有一个旋转光标,LOL,那就太酷了。特别是在“修改列”之后已使我的网站脱机。如果需要这么长时间,您的存储系统出现问题,那不是MySQL问题。我正在从varchar更改为30行表中的文本,等待了20分钟,但仍在继续。可以是什么?VARCHAR存储在表中,而文本单独存储,引用存储在表中。所以他们非常不同。我假设是因为您有一个非常大的varchar,并且您希望允许更大的值,才提示进行更改。在这种情况下,它需要移动所有的值。@AndreKR-不,我认为这很明显是MySQL的问题。将一列添加到包含900MB数据的表中(大约25K行包含较小的blob)不会花费超过3个小时,这就是我现在的工作。我的存储系统也没有问题;数据库存储在一个相当好的SSD上,我有足够的可用RAM进行缓存。我认为InnoDB在这种操作过程中很难维护索引;我想它没有优化的大容量插入机制,所以每次只重建一行新索引。@Jules你可能是对的。我想,当我写评论时,我假设它是一个MyISAM表,因为我认为“复制到tmp表”不会发生在InnoDB上,但事实上,在问题中没有给出表类型。在InnoDB中,计划几天的ALTER TABLE操作是很正常的,这就是为什么有这样的软件存在的原因。如果表上有外键,则没有任何帮助,因为无法自动重命名两个表。显然,在不更改外键的另一侧的情况下,无法更改外键一侧的数据类型。这是必然的。然后必须删除外键,更改两列,并在之后重新引入外键。应为