web-dev-qa-db-ja.com

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

〜5M行のMySQLテーブルにインデックスを作成する必要があります。これは実稼働テーブルであり、CREATE INDEXステートメントを実行すると、すべてが完全にブロックされることを恐れます...

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

停止して、インデックスを作成して、システムを再起動する必要はないのではないかと思っただけです!

88
n0cturnal

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

https://dev.mysql.com/doc/refman/5.6/en/innodb-create-index-overview.html

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

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

上記の答えから:

"5.1インデックスを超えるバージョンを使用している場合は、データベースがオンライン中に作成されます。そのため、運用システムの使用を中断する心配はありません。"

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

テーブルでUPDATE操作を行うと、インデックスの作成中に[〜#〜] block [〜#〜]が実行されます。 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番目の操作がインデックスの更新を完了するまでにかかる400msの間ブロックすることに注意してください):

 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にレプリケートされることを確認する必要があります[〜#〜] before [〜#〜]Bは最初の書き込みを取得します。 2つ以上のマスターへの同時書き込みを許可すると、... MySQLレプリケーションをDEEPレベルでよりよく理解するか、苦痛の世界に向かうことになります。極度の痛み。たとえば、オートインクリメントの列はありますか? (一方のマスターで偶数を使用し、もう一方のマスターでオッズを使用しない限り)ねじ込まれています。 「正しいことをする」ためにMySQLレプリケーションを信頼しないでください。それは賢くなく、あなたを救うことはありません。コマンドラインからバイナリトランザクションログをコピーして手動で再生するよりも、わずかに安全性が低くなります。それでも、古いマスターからすべてのクライアントを切断し、新しいマスターに切り替えることは、数時間で実行でき、数時間のスキーマのアップグレードを待つよりもはるかに高速です。
  • マスターBが新しいマスターになりました。新しいスキーマがあります。人生は素晴らしい。ビールを飲む;最悪は終わった。
  • マスターAでこのプロセスを繰り返し、彼のスキーマをアップグレードして、彼が新しいセカンダリマスターになり、プライマリマスター(マスターB)が電源を失った場合やすぐに死んでしまった場合に引き継ぎます。

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

グーグルは、私に この記事 を導き、同様の手法を説明しています。さらに良いことに、彼らは手順の同じ時点で飲むことを勧めます(記事を読む前に私の答えを書いたことに注意してください)!

Perconaのpt-online-schema-change

記事 上記でリンクしたツール pt-online-schema-change については、次のように機能します。

  • オリジナルと同じ構造を持つ新しいテーブルを作成します。
  • 新しいテーブルのスキーマを更新します。
  • 元のテーブルにトリガーを追加して、変更がコピーと同期するようにします
  • 元のテーブルからバッチで行をコピーします。
  • 元のテーブルを邪魔にならない場所に移動し、新しいテーブルに置き換えます。
  • 古いテーブルをドロップします。

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

RDS

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

111
Dave Dopson

このように ブログ投稿 アウトライン、InnoDB ALTER TABLEメカニズムはMySQL 5.6向けに完全に再設計されました。

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

インデックスをテーブルに追加するには、[ロックなしUPDATE/INSERTで結果を得るために、次のステートメント形式を使用できます。

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

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と呼びます。

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

14
Eric Saboia

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

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

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

3
Alex Le