web-dev-qa-db-ja.com

MySQLはテーブルクエリの変更が非常に遅い

このテーブルを更新して列を追加するだけで1時間以上かかるのはなぜですか?このテーブルには15M行があります。 2つのインデックスと1つのキー主キーがあります。 ALTER TABLEクエリは、1時間15分間「tmpテーブルにコピー」状態になっています。

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      |         |
+------------------+------------+----------------------+--------------+------------------------+-----------+-------------+----------+--------+------+------------+---------+
48
David Parks

MySQLのALTER TABLEのパフォーマンスは、非常に大きなテーブルでは問題になる可能性があります。 MySQLは、目的の新しい構造を持つ空のテーブルを作成し、古いテーブルのすべてのデータを新しいテーブルに挿入して、古いテーブルを削除することにより、ほとんどの変更を実行します。特にメモリが不足していて、テーブルが大きくインデックスがたくさんある場合、これには非常に長い時間がかかることがあります。多くの人が、完了までに数時間または数日かかったALTER TABLE操作の経験を持っています。

とにかく、alter tableを続行する必要がある場合、次のリソースが役立つ可能性があります。

59

ダウンタイムを気にしない場合は、3つのALTER TABLEステートメントを使用することをお勧めします。最初のステートメントは、既存のすべてのセカンダリインデックスを削除します。 2番目のステートメントは、すべての列関連の変更を適用します。最後のステートメントは、ドロップされたセカンダリインデックスを追加して戻し、他のインデックスの変更を適用します。

別の2つのヒント:

  1. インデックスの変更を適用する前に、次の2つのステートメントを実行し、インデックスの変更が完了したら値を1に戻します。

    SET unique_checks=0;
    SET foreign_key_checks=0;
    
  2. 複数のセカンダリインデックスを作成するときは、複数のALTER TABLEステートメントではなく、1つのALTER TABLEステートメントに入れます。

次の図は、パフォーマンスの違いを示しています。アプローチ1はあなたのアプローチであり、アプローチ2は私の方法です。 50mテーブルのアプローチ1と比較して、アプローチ2は約3.47%時間かかります。このソリューションは、MySQL(> = 5.5)InnoDBエンジンでのみ機能します。

enter image description here

24
Albert Wang

Perconaツールは、大きなテーブルを備えたこのようなものの命の恩人です。

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

彼らは基本的に:

  1. 複製テーブルを作成する
  2. テーブルを同期するトリガーを作成する
  3. 一括コピーデータ
  4. 確認する
  5. スワップテーブル

永遠にかかりますが、ダウンタイムなしで列を変更できることを意味するため、誰も気にしません。

8
jdwyah

あなたのテーブルには1500万行があります。 ALTER TABLEでは、テーブルからすべてのデータをコピーして、インデックスを再作成します。最初の測定として、ファイルシステムにデータファイル(MyISAMの場合はitem_catalog_map.MYD)をコピーして、どれくらい時間がかかるかを確認します。これは、ALTER TABLEが少なくともを要する時間です。

7
AndreKR

変更する大きなテーブルのロックアップを最小限に抑えるために、次のことを行います。

  • 既存のテーブルに基づいて新しい空のテーブルを作成し、この新しい空のテーブルを変更します。
  • 大きなテーブルのmysqldumpを実行して、大きなテーブルのレコードごとに1つの完全なinsertステートメントがあるようにします(スイッチ-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;
    
6
gautamc