変更したいInnoDBテーブルがあります。テーブルには約8,000万行あり、いくつかのインデックスを終了します。
列の1つの名前を変更し、さらにいくつかのインデックスを追加したいと思います。
- それを行う最速の方法は何ですか?
- 「プレーン」
alter table
、最速のソリューションですか?
この時点で、私が気にしているのは速度です:)
sent_at
、いくつかのインデックスを追加するためです
変更したいInnoDBテーブルがあります。テーブルには約8,000万行あり、いくつかのインデックスを終了します。
列の1つの名前を変更し、さらにいくつかのインデックスを追加したいと思います。
alter table
、最速のソリューションですか?この時点で、私が気にしているのは速度です:)
sent_at
、いくつかのインデックスを追加するためです
回答:
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つのインデックスの最初の列なので削除しました
他の2つのインデックスの最初の列であるため、target_persona_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;
正しい答えは、使用しているMySQLエンジンのバージョンによって異なります。
5.6以降を使用している場合、名前の変更とインデックスの追加/削除はオンラインで実行されます。つまり、すべてのテーブルのデータをコピーすることはありません。
ALTER TABLE
いつものように使用するだけで、名前の変更やインデックスの削除はほぼ瞬時に、インデックスの追加はかなり高速になります(すべてのテーブルを一度に読み取るのと同じ速さ)。
5.1+を使用していて、InnoDBプラグインが有効になっている場合は、インデックスの追加/削除もオンラインになります。名前の変更については不明です。
古いバージョンを使用している場合ALTER TABLE
は、それでも最速ですが、すべてのデータが内部の一時テーブルに再挿入されるため、恐らくひどく遅くなります。
最後に、神話を暴く時間です。残念ながら、回答にコメントするのに十分なカルマはありませんが、最も投票された回答を修正することが重要だと感じています。これは間違っています:
経験則として、MySQL Query Optimizerは、選択された列のカーディナリティがテーブルの行数の5%より大きい場合、インデックスを使用しません
それは実際には逆です。
インデックスはいくつかの行を選択するのに役立ちます。そのため、それらのカーディナリティが高いことが重要です。つまり、多くの異なる値と統計的に同じ値の行が少ないということです。
RENAME TABLE
(予想どおり)インスタントが見つかりましたがCHANGE COLUMN
、主キーの名前を変更すると、完全なコピーが作成されました... 7時間!おそらくそれが主キーだったからでしょうか?良くない。
Maria DB 10.1.12でも同じ問題があり、ドキュメントを読んだ後、「インプレース」操作を実行してテーブルのコピーを削除するオプションがあることがわかりました。このオプションを使用すると、変更テーブルは非常に高速になります。私の場合、それは:
alter table user add column (resettoken varchar(256),
resettoken_date date, resettoken_count int), algorithm=inplace;
これは非常に高速です。アルゴリズムオプションがないと、終了しません。
列の名前を変更するには、
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倍のスペースを使用することを犠牲にして、ダウンタイムを最小限に抑えることができます。
私もこの問題を抱えており、次の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;
私はそれが誰かを助けることを願っています
よろしく、
意志
SHOW CREATE TABLE tblname\G
、とのニーズが変化していること、列、列のデータ型、および列の新しい名前が表示されます。