29

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

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

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

このタスクを可能な限り迅速に実行するには、どのような設定が理想的ですか?

4

6 に答える 6

16

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

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

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

これについての素晴らしいウェビナーもここにあります

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

于 2012-07-03T20:43:32.957 に答える
15

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

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

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

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

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

それとは別に、主要なデータの移動に役立つ可能性のある 1 つのセッションだけを変更できる、いくつかのキーおよびバッファー関連の設定があります。のようなものを増やすread_rnd_buffer_sizeread_buffer_size便利でしょう。

于 2009-03-18T05:38:22.047 に答える
12
  1. スレーブの設定
  2. レプリケーションを停止します。
  3. スレーブで ALTER を作成する
  4. スレーブがマスターに追いつくようにする
  5. マスターとスレーブを交換することで、スレーブは構造が変更され、ダウンタイムが最小化された本番サーバーになります
于 2009-10-06T12:57:56.657 に答える
11

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

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

過去に私たちのために機能した別のアプローチは、変更を処理する一連の Mysql レプリカを調整することでした。私はプロセスについて話すのに最適な人物ではありませんが、基本的には、レプリケーションを 1 つのスレーブに分割し、そのインスタンスでパッチを実行し、テーブルの変更が完了したらレプリケーションを元に戻して、レプリケーションに追いつくようにすることで構成されています。レプリケーションが追いついたら、(必要に応じて) サイトをメンテナンス モードにして、マスターからこの新しいパッチ適用済みスレーブに新しいマスター データベースとして切り替えます。

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

于 2009-06-09T20:53:47.390 に答える
7

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/2007/11/03/choosing-innodb_buffer_pool_size/およびhttp://www.mysqlperformanceblog.com/2008/11/21/how-to-calculate-a-good-innodbも参照してください。 -log-file-size/ innodb およびログ サイズのチューニングに関する情報。大きなログ ファイルの欠点の 1 つは、クラッシュ後の回復時間が長くなることです。

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

  • 新たに作成されたテーブルへの単純なロード データ: 6500 秒
  • データを読み込む w. innodb_log_file_size=200M、innodb_log_buffer_size=8M、innodb_buffer_pool_size=2200M、自動コミット= 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 コア、16GB、sata コンシューマー グレード ディスク、RAID なし、データベース アクティビティなし、他のプロセス アクティビティはごくわずか、他の非常に小さな仮想マシンでのアクティビティはごくわずか。Mysql 5.1.53。初期のサーバー構成は、innodb_buffer_pool_size が 1400M に増加したことを除いて、かなりデフォルトです。変更テーブルは 2 つの小さな列を追加します。生の変更テーブルをクロックしませんでしたが、代わりに同等のロード データ インファイル ステートメントを試しました。

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

于 2012-10-02T09:57:39.500 に答える
-5

それを最適化する方法は本当にわかりませんが、通常は、そのような更新を行う前にサイトをオフライン モードにすることをお勧めします。

その後、たとえば午前 3 時に DB スクリプトを実行できるので、ダウンタイムが理想よりも長くてもあまり問題にはなりません。

于 2009-03-17T15:01:33.143 に答える