テーブルをロックせずに巨大なMySQLプロダクションテーブルにインデックスを作成する


104

約500万行のMySQLテーブルにインデックスを作成する必要があります。これはプロダクションテーブルであり、CREATE INDEXステートメントを実行すると、すべての完全なブロックを恐れます...

挿入と選択をブロックせずにそのインデックスを作成する方法はありますか?

停止してインデックスを作成し、システムを再起動する必要はありません。


1
myisam_sort_buffer_sizeとmyisam_max_sort_file_sizeが十分に大きいことを確認してください。
Jon Black

回答:


130

[2017]更新:MySQL 5.6はオンラインインデックスの更新をサポートしています

https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html#online-ddl-index-syntax-notes

MySQL 5.6以降では、インデックスが作成または削除されている間、テーブルは読み取りおよび書き込み操作に引き続き使用できます。CREATE INDEXまたはDROP INDEXステートメントは、テーブルにアクセスしているすべてのトランザクションが完了した後にのみ終了するため、インデックスの初期状態はテーブルの最新の内容を反映しています。以前は、インデックスの作成または削除中にテーブルを変更すると、通常、デッドロックが発生し、テーブルのINSERT、UPDATE、またはDELETEステートメントがキャンセルされました。

[2015] MySQL 5.5でテーブルインデックスを更新すると書き込みがブロックされる

上記の答えから:

「5.1を超えるバージョンを使用している場合、データベースがオンラインのときにインデックスが作成されます。心配しないで、本番システムの使用を中断する必要はありません。」

これは**** FALSE ****です(少なくともMyISAM / InnoDBテーブルの場合、これはそこにいる人々の99.999%が使用しているものです。ClusteredEditionは異なります。)

テーブルでUPDATE操作を実行すると、インデックスの作成中にブロックされます。MySQLはこれについて本当に(そして他にもいくつか)愚かです。

テストスクリプト:

(   
  for n in {1..50}; do
    #(time mysql -uroot -e 'select  * from website_development.users where id = 41225\G'>/dev/null) 2>&1 | grep real;
    (time mysql -uroot -e 'update website_development.users set bio="" where id = 41225\G'>/dev/null) 2>&1 | grep real;
  done
) | cat -n &
PID=$!
sleep 0.05
echo "Index Update - START"
mysql -uroot website_development -e 'alter table users add index ddopsonfu (last_name, email, first_name, confirmation_token, current_sign_in_ip);'
echo "Index Update - FINISH"
sleep 0.05
kill $PID
time mysql -uroot website_development -e 'drop index ddopsonfu on users;'

私のサーバー(InnoDB):

Server version: 5.5.25a Source distribution

出力(6番目の操作がインデックスの更新を完了するまでにかかる400ミリ秒の間どのようにブロックするかに注意してください):

 1  real    0m0.009s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.012s
 5  real    0m0.009s
Index Update - START
Index Update - FINISH
 6  real    0m0.388s
 7  real    0m0.009s
 8  real    0m0.009s
 9  real    0m0.009s
10  real    0m0.009s
11  real    0m0.009s

ブロックしない読み取り操作との比較(スクリプトのコメント行を入れ替え):

 1  real    0m0.010s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.010s
 5  real    0m0.009s
Index Update - START
 6  real    0m0.010s
 7  real    0m0.010s
 8  real    0m0.011s
 9  real    0m0.010s
...
41  real    0m0.009s
42  real    0m0.010s
43  real    0m0.009s
Index Update - FINISH
44  real    0m0.012s
45  real    0m0.009s
46  real    0m0.009s
47  real    0m0.010s
48  real    0m0.009s

ダウンタイムなしでMySQLのスキーマを更新する

これまでのところ、MySqlスキーマを更新し、可用性の停止に悩まされない方法は1つしかありません。循環マスター:

  • マスターAはMySQLデータベースを実行しています
  • マスターBを稼働させ、マスターAからの書き込みを複製させる(BはAのスレーブ)
  • マスターBでスキーマの更新を実行します。アップグレード中に遅れます
  • マスターBに追いつきましょう。不変:スキーマの変更は、ダウンバージョンスキーマから複製されたコマンドを処理できる必要があります。インデックス作成の変更が適用されます。通常、単純な列の追加が適しています。列を削除しますか?おそらく違います。
  • すべてのクライアントをマスターAからマスターBに原子的に交換します。安全にしたい場合は(信頼してください)、Aへの最後の書き込みがBにレプリケートされる前に確認する必要があります。Bが最初の書き込みを行います。2つ以上のマスターへの同時書き込みを許可すると、... DEEPレベルでのMySQLレプリケーションの理解が深まるか、苦痛の世界に向かっています。極度の痛み。AUTOINCREMENTというカラムはありますか??? あなたはねじ込まれています(一方のマスターで偶数を使用し、もう一方のマスターでオッズを使用しない限り)。「正しいこと」を行うためにMySQLレプリケーションを信頼しないでください。それは賢くなく、あなたを救うことはありません。コマンドラインからバイナリトランザクションログをコピーして手動で再生するよりも、少し安全性が低くなります。それでも、すべてのクライアントを古いマスターから切断し、新しいマスターに切り替えるのはほんの数秒で実行でき、数時間のスキーマアップグレードを待つよりもはるかに高速です。
  • これでマスターBが新しいマスターになります。新しいスキーマがあります。人生は素晴らしい。ビールを飲む; 最悪は終わった。
  • マスターAでこのプロセスを繰り返し、スキーマをアップグレードして新しいセカンダリマスターになるようにします。プライマリマスター(マスターBが今すぐ)が電源を失うか、すぐに停止した場合に引き継ぐ準備ができています。

これはスキーマを更新する簡単な方法ではありません。深刻な本番環境で実行可能。はい、そうです。書き込みをブロックせずにMySQLテーブルにインデックスを追加する簡単な方法がある場合は、お知らせください。

グーグルで私に似たテクニックを説明するこの記事を紹介しました。さらに良いことに、彼らは手順の同じ時点で飲むことを勧めています(記事を読む前に回答を書いたことに注意してください)!

ペルコナのpt-online-schema-change

記事私はツールについての協議の上リンクは、PT-オンライン・スキーマの変更は、その作品次のように:

  • 元と同じ構造の新しいテーブルを作成します。
  • 新しいテーブルのスキーマを更新します。
  • 変更がコピーと同期して維持されるように、元のテーブルにトリガーを追加します
  • 元のテーブルから行をバッチでコピーします。
  • 元のテーブルを邪魔にならない場所に移動し、新しいテーブルと交換します。
  • 古いテーブルを削除します。

私はこのツールを自分で試したことはありません。YMMV

RDS

現在、AmazonのRDSを通じてMySQLを使用しています。これは、MySQLをラップして管理する本当に気の利いたサービスであり、ボタン1つで新しいリードレプリカを追加し、ハードウェアSKU間でデータベースを透過的にアップグレードできます。本当に便利です。データベースへのSUPERアクセスを取得できないため、レプリケーションを直接実行することはできません(これは幸運なのでしょうか、それとも呪いなのでしょうか?)。ただし、リードレプリカの昇格を使用して、読み取り専用スレーブでスキーマを変更してから、そのスレーブを昇格して新しいマスターにすることができます。上で説明したのとまったく同じトリックで、実行が非常に簡単です。彼らはまだカットオーバーであなたを助けるために多くをしません。アプリを再構成して再起動する必要があります。


3
pt-online-schema-changeは、マスター/スレーブレプリケーションでもうまく機能します。私はこれを使用して、2つのレプリケーションスレーブを持つ運用マスターdbの20M +レコードのビジーな読み取りテーブルで、一時的な中断やダウンタイムなしにライブマイグレーションを実行しました。スクリプトの準備には少し時間がかかりますが、通常、生のSQLの変更を含む.sqlファイルと、同じSQLを実行するフラグメント形式(ALTER TABLEなし)でラッパーとして.shファイルを作成する必要があります。pt-online-schema-changeを使用して複数のコマンドを実行するには、それらの文字列をコンマで区切って並べます。
Alex Le

-1; 古いバージョンについては知りませんが、MySQL 5.6+(この回答が書かれたときにRCが存在し、この回答が続いたときに公式にリリースされたもの)でインデックスの作成が同時DMLをブロックしないことは知っています2013年5月に編集されました)挿入を受け入れながら、これに頼って本番テーブルで数時間のインデックス作成を実行しました。そして、インデックス作成が5.5以下でDMLをブロックすることについて正しいかもしれませんが、ここで示されている1秒未満の遅延は完全に説得力があるわけではありません。
Mark Amery 2017年

@MarkAmery-ブロッキング動作はブロッキング動作であり、400msは永遠です。MySQL 5.5はインデックスの更新をブロックします。より大きなテストデータベースを構築すると、数秒、数時間、または数日間ブロックされます。MySQL 5.6でオンラインスキーマが更新される前にこの投稿を書いたので、私の元のコンテンツにはその事実が反映されていません。新しく利用できる情報を反映するように投稿を更新しました。
Dave Dopson、2017年

@ DaveDopson、UPDATEオペレーションのみがブロックされることを100%確信していますか?
toto_tico

私がテストしたバージョンがそうでした。
Dave Dopson

67

このブログ投稿の概要にあるように、InnoDB ALTER TABLEメカニズムはMySQL 5.6用に完全に再設計されています。

(このトピックの独占的な概要については、MySQLのドキュメントで午後に読む価値があります。)

テーブルにインデックスを追加するには、ロックせずに結果UPDATE/ INSERT、次の文の形式を使用することができます。

ALTER TABLE my_table ADD INDEX my_table__idx (my_column), ALGORITHM=INPLACE, LOCK=NONE;


16

MySQLの5.6アップデート(2013年2月):インデックスもInnoDBテーブルを使用して作成されている間、あなたは今、読み取りおよび書き込み操作を行うことができます- http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index -overview.html

MySQL 5.6以降では、インデックスが作成または削除されている間、テーブルは読み取りおよび書き込み操作に引き続き使用できます。CREATE INDEXまたはDROP INDEXステートメントは、テーブルにアクセスしているすべてのトランザクションが完了した後にのみ終了するため、インデックスの初期状態はテーブルの最新の内容を反映しています。以前は、インデックスの作成または削除中にテーブルを変更すると、通常、デッドロックが発生し、テーブルのINSERT、UPDATE、またはDELETEステートメントがキャンセルされました。

そして:

MySQL 5.6では、この機能がより一般的になりました。インデックスの作成中にテーブルの読み取りと書き込みを行うことができ、テーブルをコピーすることなく、DML操作をブロックすることなく、またはその両方で、より多くの種類のALTER TABLE操作を実行できます。したがって、MySQL 5.6以降では、通常、この機能セットを高速インデックス作成ではなくオンラインDDLと呼びます。

http://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_fast_index_creationから


では、Daveの分析はどのように説明できますか?
Nikhil Sahu 2017

1
@NikhilSahu Daveは明らかにMySQL 5.6ではテストしていませんでしたが、一部の古いバージョンではテストしていました。Daveが回答の最初の改訂版を投稿した時点では、5.6はまだリリースされていません。
Mark Amery 2017年

+1。私の分析はMySQL 5.5(2013年に利用可能となった最新のもの)に関するものでした。MySQL 5.6の新機能を反映するように回答を更新しています。
Dave Dopson、2017年

3

pt-online-schema-changeは、移行によってサイトがダウンしないことを確認したい場合に使用する方法です。

上記のコメントで書いたように、私は本番環境でのpt-online-schema-changeに関するいくつかの経験を持っています。20M +レコードのメインテーブルとマスター-> 2つの読み取り専用レプリケーションスレーブがあります。新しい列の追加から文字セットの変更、いくつかのインデックスの追加まで、pt-online-schema-changeで少なくとも数十の移行を実行しました。移行期間中も大量のトラフィックを処理しており、問題は発生していません。もちろん、運用環境で実行する前に、すべてのスクリプトを徹底的にテストする必要があります。

pt-online-schema-changeがデータを1回コピーするだけで済むように、変更を1つのスクリプトにまとめようとしました。また、データが失われるため、列名の変更には十分注意してください。ただし、インデックスの追加は問題ありません。


の無条件の推奨に同意しませんpt-online-schema-change。これはすばらしいことですが、MySQL 5.6+のオンラインDDL機能がすでに正常に機能している多くの状況ではやり過ぎです。また、(トリガーでうまく機能しないなどの)制限があり、スキーマの変更が進行している間、元のテーブルへの挿入ごとに必要な書き込み量が2倍になります。通常のオンラインスキーマの変更よりもディスクにかなりの負荷がかかるため、単純な方法でスキーマの変更を実行するだけで問題がなかった場合に、「サイトをダウンさせる」可能性があります。
Mark Amery 2017年

当時のpt-online-schema-changeの実際の経験に基づいて書いたので、なぜ私の推奨を「非修飾」と呼ぶのかわかりません。私がスキーマ変更を実行したときはいつでも、サイトに少なくとも1000人以上の訪問者がいて、もちろんディスクIOに負担がかかっていましたが、サイトはダウンしませんでした。良いキャッシングがあることも助けになった。私はMySQL 5.6+オンラインDDLを使用していませんが、私の経験から、pt-online-schema-changeは私たちのケースではうまく機能しました。
Alex Le

1
@AlexYe Yikes、私は「コメントする資格のない誰かによって提供される」という意味ではなく、「予約なし」という意味で「資格なし」を意味しました-後者の解釈は、私があなたのコメントを見て間違いなくそうなるまで起こりませんでした私が意図したものではありません!つまり、pt-online-schema-changeは便利なツールですが、通常のオンラインDDLが優れていて、それが一握りである状況は非常に多いため、推奨事項は普遍的なものではなく、慎重に公開する必要があります。
Mark Amery 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.