MySQL-InnoDBのALTER TABLEへの最速の方法


12

変更したいInnoDBテーブルがあります。テーブルには約8,000万行あり、いくつかのインデックスを終了します。

列の1つの名前を変更し、さらにいくつかのインデックスを追加したいと思います。

  • それを行う最速の方法は何ですか?
  • 「プレーン」alter table、最速のソリューションですか?

この時点で、私が気にしているのは速度です:)


してくださいSHOW CREATE TABLE tblname\G、とのニーズが変化していること、列、列のデータ型、および列の新しい名前が表示されます。
RolandoMySQLDBA 2011

ここにそれがあります:pastie.org/3078349名前を変更する必要のある列はsent_at、いくつかのインデックスを追加するためです
Ran

sent_atの名前をwhatに変更する必要がありますか?
RolandoMySQLDBA '12

言うことができます:new_sent_at
Ran

回答:


14

ALTER TABLEを高速化する確実な方法の1つは、不要なインデックスを削除することです

新しいバージョンのテーブルをロードするための最初のステップは次のとおりです

CREATE TABLE s_relations_new LIKE s_relations;
#
# Drop Duplicate Indexes
#
ALTER TABLE s_relations_new
    DROP INDEX source_persona_index,
    DROP INDEX target_persona_index,
    DROP INDEX target_persona_relation_type_index
;

次の点に注意してください:

  • source_persona_indexは他の4つのインデックスの最初の列なので削除しました

    • unique_target_persona
    • unique_target_object
    • source_and_target_object_index
    • source_target_persona_index
  • 他の2つのインデックスの最初の列であるため、target_persona_indexを削除しました

    • target_persona_relation_type_index
    • target_persona_relation_type_message_id_index
  • 最初の2列もtarget_persona_relation_type_message_id_indexにあるため、target_persona_relation_type_indexを削除しました

OK不要なインデックスを処理します。カーディナリティが低いインデックスはありますか?これを確認する方法は次のとおりです。

次のクエリを実行します。

SELECT COUNT(DISTINCT sent_at)               FROM s_relations;
SELECT COUNT(DISTINCT message_id)            FROM s_relations;
SELECT COUNT(DISTINCT target_object_id)      FROM s_relations;

あなたの質問によると、約80,000,000行があります。経験則として、MySQL Query Optimizerは、選択された列のカーディナリティがテーブル行カウントの5%より大きい場合、インデックスを使用しません。この場合、4,000,000になります。

  • COUNT(DISTINCT sent_at)> 4,000,000の 場合
    • その後 ALTER TABLE s_relations_new DROP INDEX sent_at_index;
  • COUNT(DISTINCT message_id)> 4,000,000の 場合
    • その後 ALTER TABLE s_relations_new DROP INDEX message_id_index;
  • COUNT(DISTINCT target_object_id)> 4,000,000の 場合
    • その後 ALTER TABLE s_relations_new DROP INDEX target_object_index;

これらのインデックスの有用性または無用性が決定したら、データを再ロードできます

#
# Change the Column Name
# Load the Table
#
ALTER TABLE s_relations_new CHANGE sent_at sent_at_new int(11) DEFAULT NULL;
INSERT INTO s_relations_new SELECT * FROM s_relations;

それだけですよね?いいえ!!!

この間ずっとWebサイトが稼働している場合、s_relations_newのロード中にs_relationsに対してINSERTが実行されている可能性があります。欠落している行をどのように取得できますか?

s_relations_newで最大IDを見つけて、s_relationsからそのIDの後にすべてを追加します。テーブルが確実に凍結され、この更新のみに使用されるようにするには、s_relation_newに挿入された最後の行を取得するために、少しのダウンタイムが必要です。ここではあなたが何をすべきかです:

OSでmysqlを再起動して、root @ localhost以外の誰もログインできないようにします(TCP / IPを無効にします)。

$ service mysql restart --skip-networking

次に、mysqlにログインし、最後の行をロードします。

mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> ALTER TABLE s_relations_new RENAME s_relations;

次に、mysqlを通常どおり再起動します

$ service mysql restart

ここで、mysqlを停止できない場合は、s_relationsでベイトアンドスイッチを実行する必要があります。mysqlにログインして、次の手順を実行します。

mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations_old WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations_new RENAME s_relations;

試してみる !!!

警告:この操作に満足したら、できるだけ早く古いテーブルを削除できます。

mysql> DROP TABLE s_relations_old;

12

正しい答えは、使用しているMySQLエンジンのバージョンによって異なります。

5.6以降を使用している場合、名前の変更とインデックスの追加/削除はオンラインで実行されます。つまり、すべてのテーブルのデータをコピーすることはありません。

ALTER TABLEいつものように使用するだけで、名前の変更やインデックスの削除はほぼ瞬時に、インデックスの追加はかなり高速になります(すべてのテーブルを一度に読み取るのと同じ速さ)。

5.1+を使用していて、InnoDBプラグインが有効になっている場合は、インデックスの追加/削除もオンラインになります。名前の変更については不明です。

古いバージョンを使用している場合ALTER TABLEは、それでも最速ですが、すべてのデータが内部の一時テーブルに再挿入されるため、恐らくひどく遅くなります

最後に、神話を暴く時間です。残念ながら、回答にコメントするのに十分なカルマはありませんが、最も投票された回答を修正することが重要だと感じています。これは間違っています:

経験則として、MySQL Query Optimizerは、選択された列のカーディナリティがテーブルの行数の5%より大きい場合、インデックスを使用しません

それは実際にはです。

インデックスはいくつかの行を選択するのに役立ちます。そのため、それらのカーディナリティが高いことが重要です。つまり、多くの異なる値と統計的に同じ値の行が少ないということです。


InnoDBプラグインのドキュメントへのリンク(担当者の制限により貼り付けられませんでした)。
mezis 2013

2
MySQL 5.5では、RENAME TABLE(予想どおり)インスタントが見つかりましたがCHANGE COLUMN、主キーの名前を変更すると、完全なコピーが作成されました... 7時間!おそらくそれが主キーだったからでしょうか?良くない。
KCD

2

Maria DB 10.1.12でも同じ問題があり、ドキュメントを読んだ後、「インプレース」操作を実行してテーブルのコピーを削除するオプションがあることがわかりました。このオプションを使用すると、変更テーブルは非常に高速になります。私の場合、それは:

alter table user add column (resettoken varchar(256),
  resettoken_date date, resettoken_count int), algorithm=inplace;

これは非常に高速です。アルゴリズムオプションがないと、終了しません。

https://mariadb.com/kb/en/mariadb/alter-table/


0

列の名前を変更するには、

ALTER TABLE tablename CHANGE columnname newcolumnname datatype;

正常で、ダウンタイムは発生しません。

インデックスの場合、CREATE INDEXステートメントはテーブルをロックします。あなたが言ったようにそれが未使用のスレーブであれば、それは問題ではありません。

他の1つのオプションは、適切な列名とインデックスを持つ真新しいテーブルを作成することです。次に、すべてのデータをそこにコピーしてから、一連の

BEGIN TRAN;
ALTER TABLE RENAME tablename tablenameold;
ALTER TABLE RENAME newtablename tablename;
DROP TABLE tablenameold;
COMMIT TRAN;

これにより、一時的に2倍のスペースを使用することを犠牲にして、ダウンタイムを最小限に抑えることができます。


1
MySQLのDDLはトランザクション対応ではありません。各DDLステートメントはCOMMITをトリガーします。私はこれについて書きました:dba.stackexchange.com/a/36799/877
RolandoMySQLDBA '8/02/14

0

私もこの問題を抱えており、次のSQLを使用しました。

/*on créé la table COPY SANS les nouveaux champs et SANS les FKs */
CREATE TABLE IF NOT EXISTS prestations_copy LIKE prestations;

/* on supprime les FKs de la table actuelle */
ALTER TABLE `prestations`
DROP FOREIGN KEY `fk_prestations_pres_promos`,
DROP FOREIGN KEY `fk_prestations_activites`;

/* on remet les FKs sur la table copy */
ALTER TABLE prestations_copy 
    ADD CONSTRAINT `fk_prestations_activites` FOREIGN KEY (`act_id`) REFERENCES `activites` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION,
    ADD CONSTRAINT `fk_prestations_pres_promos` FOREIGN KEY (`presp_id`) REFERENCES `pres_promos` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION;

/* On fait le transfert des données de la table actuelle vers la copy, ATTENTION: il faut le même nombre de colonnes */
INSERT INTO prestations_copy
SELECT * FROM prestations;

/* On modifie notre table copy de la façon que l'on souhaite */
ALTER TABLE `prestations_copy`
    ADD COLUMN `seo_mot_clef` VARCHAR(50) NULL;

/* on supprime la table actuelle et renome la copy avec le bon nom de table */
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE prestations;
RENAME TABLE prestations_copy TO prestations;
SET FOREIGN_KEY_CHECKS=1;   

私はそれが誰かを助けることを願っています

よろしく、

意志

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.