web-dev-qa-db-ja.com

InnoDBのALTERTABLE用にMySQLを最適化する

近いうちに、本番データベースにスキーマの変更を加える必要があります。この作業のためにダウンタイムを最小限に抑える必要がありますが、ALTERTABLEステートメントはかなり長い間実行されます。最大のテーブルには1億5000万のレコードがあり、最大のテーブルファイルは50Gです。すべてのテーブルはInnoDBであり、(テーブルごとのファイルではなく)1つのビッグデータファイルとして設定されました。 MySQL 5.0.46は、8コアのマシン、16Gメモリ、およびRAID10構成で実行されています。

MySQLのチューニングの経験はありますが、これは通常、複数のクライアントからの読み取りまたは書き込みに焦点を当てています。このテーマに関してインターネット上には多くの情報がありますが、MySQLサーバーを(一時的に)調整してInnoDBテーブルのALTERTABLEを高速化するためのベストプラクティスやINSERTINTOについて利用できる情報はほとんどないようです。 。SELECT FROM(ALTER TABLEの代わりにこれを使用して、処理を少し高速化する機会を増やします)。

計画しているスキーマの変更は、すべてのテーブルに整数列を追加し、それを現在の主キーではなく主キーにすることです。 'old'列も保持する必要があるため、既存の値を上書きすることはできません。

このタスクをできるだけ早く実行するための理想的な設定は何でしょうか?

29
schuilr

Perconaツールキットの pt-online-schema-change を確認することをお勧めします。基本的にそれが行うことは次のとおりです。

  • 元のテーブル構造をコピーし、ALTERを実行します。
  • 古いテーブルから新しく作成されたテーブルに行をコピーします。
  • トリガーを使用して、コピー中に変更を追跡および同期します。
  • すべてが終了すると、両方の名前を変更してテーブルを交換します。

シングルインスタンスデータベースでは非常にうまく機能しますが、レプリケーションを使用していて、スレーブを停止して後で再構築する余裕がない場合は、かなり注意が必要な場合があります。

これについての素敵なウェビナーもあります ここ

PS:古い質問だと思います。誰かが検索エンジンを介してこれにぶつかった場合に備えて、答えるだけです。

15

要件についてもう少し慎重に考える必要があります。

最も単純なレベルでは、テーブルを変更する「最速の」方法は、できるだけ少ないALTER TABLEステートメント、できれば1つで変更することです。これは、MySQLがテーブルのデータをコピーしてスキーマを変更し、15の変更を行う一方で、1つのコピーを作成する方が、テーブルを15回コピーして、一度に1つの変更を行うよりも明らかに(実際には)高速であるためです。

しかし、最小限のダウンタイムでこの変更を行う方法を求めているのではないかと思います。私がそうする方法では、基本的に、非ブロックALTER TABLEが機能する方法を合成します。ただし、いくつかの追加要件があります。

  1. 後者の場合は「変更された」日付フィールド、前者の場合はAUTO_INCREMENTフィールドなど、追加および変更されたデータを追跡する方法が必要です。
  2. データベースにテーブルのコピーを2つ持つには、スペースが必要です。
  3. テーブルへの変更がスナップショットよりもはるかに先に進まない期間が必要です

基本的なテクニックは、あなたが提案したとおりです。つまり、INSERT INTO ... SELECT ...を使用します。少なくとも、InnoDBテーブルから始めているので、前にいるので、SELECTはブロックされません。新しい空のテーブルでALTER TABLEを実行することをお勧めします。これにより、MySQLがすべてのデータを再度コピーする必要がなくなります。つまり、INSERT INTO ... SELECT ...ステートメントにすべてのフィールドを正しくリストする必要があります。次に、単純なRENAMEステートメントを実行してスワップします。次に、変更されたすべてのデータを取得するために、別のINSERT INTO ... SELECT ... WHERE ...とおそらくUPDATE ... INNER JOIN ... WHERE ...を実行する必要があります。 INSERTUPDATEquicklyを実行する必要があります。そうしないと、コードがスナップショットに新しい行と更新を追加し始めますwill更新を妨害します。RENAMEの前から数分間アプリをメンテナンスモードにできれば、この問題は発生しません。)

それとは別に、メインデータの移動に役立つ可能性のある1つのセッションで変更できるキーとバッファーに関連する設定がいくつかあります。 read_rnd_buffer_sizeread_buffer_sizeのようなものは増やすのに役立ちます。

15
staticsan
  1. スレーブのセットアップ
  2. レプリケーションを停止します。
  3. スレーブでALTERを作成する
  4. スレーブがマスターに追いつくようにします
  5. マスターとスレーブを交換するため、スレーブは構造が変更され、ダウンタイムが最小の本番サーバーになります
12
noonex

残念ながら、これは必ずしも staticsan が彼の答えにつながるほど単純ではありません。オンライン中に新しいテーブルを作成してデータを移動するのは簡単で、メンテナンスモードでクリーンアップを実行することもできますが、MysqlRENAME操作は古いテーブルへの外部キー参照を自動的に操作します。これが意味するのは、元のテーブルへの外部キー参照は、テーブルの名前を変更したものを指しているということです。

したがって、変更しようとしているテーブルへの外部キー参照がある場合は、それらのテーブルを変更して新しいテーブルへの参照を置き換えるか、さらに悪いことに、そのテーブルが大きい場合は、大きなプロセスを繰り返す必要があります。テーブル番号2。

過去に私たちのために働いた別のアプローチは、変更を処理するMysqlレプリカのセットをジャグリングすることでした。私はプロセスについて話すのに最適な人物ではありませんが、基本的には1つのスレーブへのレプリケーションを中断し、そのインスタンスでパッチを実行し、変更テーブルが完了したらレプリケーションをオンに戻してレプリケーションに追いつくことで構成されます。レプリケーションが追いついたら、サイトをメンテナンスモードにして(必要な場合)、マスターからこの新しいパッチが適用されたスレーブに新しいマスターデータベースとして切り替えます。

私が覚えていない唯一のことは、他のスレーブを新しいマスターに向けて、変更が適用されるようにするときです。このプロセスの1つの注意点として、通常、コードを変更する必要がある前、またはコードが変更されて列/キーを参照しなくなった後に、これを使用してパッチを変更します。

11
RC.

1つの変更テーブルを高速化するためにさまざまな戦略をテストしました。最終的に、特定のケースでは約10倍の速度向上が得られました。結果はあなたの状況に当てはまるかもしれませんし、当てはまらないかもしれません。ただし、これに基づいて、InnoDBログファイル/バッファーサイズパラメーターを試してみることをお勧めします。

要するに、innodb_log_file_sizeとinnodb_log_buffer_sizeを増やすだけで測定可能な効果がありました(注意してください!innodb_log_file_sizeを変更するのは危険です。詳細については以下をご覧ください)。

大まかな書き込みデータレート(iostat)とCPUアクティビティに基づくと、ボトルネックはioベースでしたが、データスループットではありませんでした。より高速な500の実行では、書き込みスループットは少なくともハードディスクに期待されるのと同じ球場にあります。

パフォーマンスの最適化を試みました:

innodb_log_file_sizeの変更は危険な場合があります。http://www.mysqlperformanceblog.com/2011/07/09/how-to-changeを参照) -innodb_log_file_size-safely / リンクで説明されている手法(ファイル移動)は、私の場合はうまく機能しました。

http://www.mysqlperformanceblog.com/2007/11/03/choosing-innodb_buffer_pool_size/ および http://www.mysqlperformanceblog.com/2008/11/21/)も参照してください。 innodbとログサイズの調整については、how-to-calculate-a-good-innodb-log-file-size / を参照してください。ログファイルが大きい場合の欠点の1つは、クラッシュ後の回復時間が長くなることです。

テストの実行と大まかなタイミング:

  • 新しく作成された広告テーブルへの単純なデータのロード:6500
  • データのロードw。 innodb_log_file_size = 200M、innodb_log_buffer_size = 8M、innodb_buffer_pool_size = 220M、autocommit = 0; unique_checks = 0、foreign_key_checks = 0:500秒
  • データのロードw。 innodb_log_file_size = 200M、innodb_log_buffer_size = 8M:500秒
  • 同等のストレートアルターテーブルw。 datainnodb_log_file_size = 200M、innodb_log_buffer_size = 8M:500秒

テストの詳細:テーブル:InnoDB、6M行、ディスク上の2.8G、単一ファイル(innodb_file_per_tableオプション)、主キーは1整数、+ 2一意の制約/インデックス、8列、平均行の長さは218バイトです。サーバー:Ubuntu 12.04、x86_64、仮想マシン、8コア、16 GB、sataコンシューマーグレードディスク、RAIDなし、データベースアクティビティなし、その他のプロセスアクティビティはごくわずか、その他のはるかに小さい仮想マシンでのアクティビティはごくわずかです。 Mysql5.1.53。 innodb_buffer_pool_sizeが1400Mに増加したことを除いて、初期サーバー構成はかなりデフォルトです。 ALTER TABLEは、2つの小さな列を追加します。私は生のALTERテーブルをクロックしませんでしたが、代わりに同等のロードデータinfileステートメントを試し、最終的にストレートALTERテーブルを実行して同等の結果を得ました。

この質問は、少なくとも次の質問に関連しています:

5
Peter Lamberg