web-dev-qa-db-ja.com

テーブルをロックせずにALTER TABLE?

MySQLでALTER TABLEステートメントを実行すると、ステートメント全体の間、テーブル全体が読み取りロックされます。大きなテーブルの場合、挿入または更新ステートメントが1時間ロックされる可能性があります。プロセス全体でテーブルが更新可能になるように列を追加するなど、「ホット変更」を行う方法はありますか?

私は主にMySQLのソリューションに興味がありますが、MySQLでできない場合は他のRDBMSに興味があります。

明確にするために、私の目的は、余分なテーブル列を必要とする新しい機能が実稼働にプッシュされるときのダウンタイムを回避することです。任意のデータベーススキーマwill時間が経つにつれて変化します。これは単なる事実です。これらの変更が必然的にダウンタイムにつながることを受け入れる必要がある理由がわかりません。それはただ弱いです。

102
Daniel

他の唯一のオプションは、とにかく多くのRDBMSシステムが行うことを手動で行うことです...
-新しいテーブルを作成する

その後、古いテーブルの内容を一度にチャンクにコピーできます。ソーステーブルのINSERT/UPDATE/DELETEには常に注意しながら注意してください。 (トリガーで管理できます。これにより速度が低下しますが、ロックではありません...)

終了したら、ソーステーブルの名前を変更してから、新しいテーブルの名前を変更します。できればトランザクションで。

終了したら、そのテーブルを使用するストアドプロシージャなどを再コンパイルします。実行計画はおそらく無効になります。

編集:

この制限が少し悪いというコメントがいくつかあります。だから私はそれがどうであるかを示すために新しい視点を置くと思った...

  • 新しいフィールドの追加は、すべての行で1つのフィールドを変更するようなものです。
  • フィールドロックは行ロックよりもはるかに難しく、テーブルロックを気にしないでください。

  • 実際にディスク上の物理構造を変更しているため、すべてのレコードが移動します。
  • これはテーブル全体の更新のようなものですが、より大きな影響があります...
58
MatBailie

Perconaは、これを可能にする pt-online-schema-change というツールを作成します。

基本的に、テーブルのコピーを作成し、新しいテーブルを変更します。新しいテーブルを元のテーブルと同期させるには、トリガーを使用して更新します。これにより、新しいテーブルがバックグラウンドで準備されている間に、元のテーブルにアクセスできます。

これは上記のDemsが推奨する方法に似ていますが、これは自動化された方法で行われます。

一部のツールには学習曲線があります。つまり、データベースに接続しますが、いったんダウンすると、素晴らしいツールになります。

例:

pt-online-schema-change --alter "ADD COLUMN c1 INT" D=db,t=numbers_are_friends
39
SeanDowney

2009年のこの質問。MySQLはソリューションを提供します。

オンラインDDL

DDL(主にALTER TABLE)操作中にInnoDBテーブルのパフォーマンス、同時実行性、および可用性を改善する機能。詳細については、セクション14.11「InnoDBおよびオンラインDDL」を参照してください。

詳細は、操作のタイプによって異なります。場合によっては、ALTER TABLEの進行中にテーブルを同時に変更できます。この操作は、テーブルコピーを実行せずに、または特別に最適化されたタイプのテーブルコピーを使用せずに実行できる場合があります。スペース使用量は、innodb_online_alter_log_max_size構成オプションによって制御されます。

テーブルへのアクセスを完全にブロックするか(LOCK = EXCLUSIVE句)、クエリを許可するがDMLを許可しない(LOCK = SHARED句)か、完全なクエリとDMLを許可するかを選択することにより、DDL操作中のパフォーマンスと同時実行性のバランスを調整できますテーブルへのアクセス(LOCK = NONE句)。 LOCK句を省略するか、LOCK = DEFAULTを指定すると、MySQLは操作の種類に応じて可能な限り多くの同時実行を許可します。

テーブルの新しいコピーを作成するのではなく、可能な限りインプレースで変更を実行することで、テーブルのコピーとセカンダリインデックスの再構築に関連するディスク領域の使用率とI/Oオーバーヘッドの一時的な増加を回避できます。

詳細については、 MySQL 5.6リファレンスマニュアル-> InnoDBおよびオンラインDDL を参照してください。

MariaDBでもオンラインDDLを利用できるようです

または、ALTER ONLINE TABLEを使用して、ALTER TABLEが同時操作をブロックしないようにします(ロックを取得しません)。 LOCK = NONEと同等です。

ALTER TABLEに関するMariaDB KB

19
Ivanov

Facebookのオンラインスキーマ変更ツールをご覧ください。

http://www.facebook.com/notes/mysql-at-facebook/online-schema-change-for-mysql/430801045932

気弱な人向けではありません。しかし、それは仕事をします。

16
Steven Soroka

それがオプションであれば、Postgresをお勧めします。 postgresでは、次の手順でダウンタイムは本質的にありません。

その他の優れた機能は、ほとんどのDDLステートメントがトランザクションであるため、SQLトランザクション内で全体の移行を実行でき、何か問題が発生した場合、全体がロールバックされることです。

私は this を少し前に書きました。おそらく他のメリットについてもう少し洞察を得ることができるでしょう。

14
mikelikespie

他のデータベースについて尋ねたので、ここにOracleに関する情報があります。

NULL列をOracleテーブルに追加すると、データディクショナリのみが更新されるため、非常に迅速な操作です。これにより、非常に短時間テーブルの排他ロックが保持されます。ただし、依存関係のあるストアドプロシージャ、ビュー、トリガーなどは無効になります。これらは自動的に再コンパイルされます。

必要に応じて、ONLINE句を使用してインデックスを作成できます。繰り返しますが、非常に短いデータ辞書ロックのみです。テーブル全体を読み取ってインデックス付けするものを探しますが、これを行っている間は誰もブロックしません。

外部キーを追加する必要がある場合、これを実行して、データが正しいことをOracleに信頼させることができます。それ以外の場合は、テーブル全体を読み取り、遅い可能性があるすべての値を検証する必要があります(最初にインデックスを作成します)。

デフォルト値または計算値を新しい列のすべての行に配置する必要がある場合、大規模な更新を実行するか、新しいデータを入力する小さなユーティリティプログラムを実行する必要があります。これは、特に行が大きくなり、ブロックに収まらなくなった場合に遅くなる可能性があります。このプロセス中にロックを管理できます。まだ実行中のアプリケーションの古いバージョンはこの列を知らないため、不正なトリガーが必要な場合やデフォルトを指定する必要がある場合があります。

そこから、アプリケーションサーバー上で新しいバージョンのコードに切り替えて、コードを実行し続けることができます。卑劣なトリガーをドロップします。

または、DBMS_REDEFINITIONを使用できます。DBMS_REDEFINITIONは、この種のことを行うために設計されたブラックボックスです。

これはすべてテストするのが非常に面倒なので、メジャーバージョンをリリースするたびに日曜日の早朝の停止が発生するだけです。

7
WW.

アプリケーションの更新時にデータベースのダウンタイムを許容できない場合は、高可用性のために2ノードクラスターを維持することを検討する必要があります。シンプルなレプリケーションセットアップを使用すると、提案するような、ほぼ完全にオンラインの構造変更を行うことができます。

  • すべての変更がパッシブスレーブに複製されるのを待ちます
  • パッシブスレーブをアクティブマスターに変更します
  • 古いマスターの構造を変更します
  • 変更を新しいマスターから古いマスターに複製します
  • マスタースワッピングと新しいアプリの展開を同時に実行します

必ずしも簡単ではありませんが、通常はダウンタイムなしで機能します! 2番目のノードはパッシブノードである必要はなく、テスト、統計の実行、またはフォールバックノードとして使用できます。インフラストラクチャがない場合は、単一のマシン(MySQLの2つのインスタンス)内でレプリケーションをセットアップできます。

3
jynus

いや。 MyISAMテーブルを使用している場合、私の知る限り、テーブルロックのみを実行します。レコードロックはありません。単純にすべてを超高速に維持しようとします。 (他のMySQLテーブルの動作は異なります。)いずれの場合でも、テーブルを別のテーブルにコピーして変更し、次にそれらを切り替えて、差分を更新できます。

これは非常に大きな変更であるため、DBMSがそれをサポートすることはないでしょう。そもそもテーブルのデータを使用してそれを行うことができることは、利点と考えられています。

2
dkretz

一時的な解決策...

他の解決策として、元のテーブルの主キーを持つ別のテーブルを新しい列とともに追加することもできます。

主キーを新しいテーブルに設定し、新しいテーブルの新しい列の値を設定し、選択操作のためにこのテーブルを結合するようにクエリを変更します。また、この列の値に対して個別に挿入、更新する必要があります。

ダウンタイムを取得できる場合、元のテーブルを変更し、DMLクエリを変更し、以前に作成した新しいテーブルを削除できます。

そうでない場合は、perconaのクラスタリング手法、レプリケーション、pt-online-schemaツールを使用できます

2
Balasundaram

Innodbプラグインを使用すると、セカンダリインデックスの追加または削除のみを行うALTER TABLEステートメントを「すばやく」、つまりテーブルを再構築せずに実行できます。

ただし、一般的に言えば、MySQLでは、ALTER TABLEにはテーブル全体の再構築が含まれ、非常に長い時間がかかる可能性があります(つまり、テーブルに有用な量のデータがある場合)。

ALTER TABLEステートメントを定期的に実行する必要がないように、アプリケーションを本当に設計する必要があります。待機する準備ができていない場合、または小さなテーブルを変更している場合を除き、アプリケーションの通常の実行中にALTER TABLEを実行したくないことは確かです。

1
MarkR

この点でPostgresとMySQLの違いは、Postgresではテーブルを再作成せず、Oracleに似たデータディクショナリを変更することです。したがって、操作は高速ですが、他の人が前述したように、非常に短い時間だけ排他的なDDLテーブルロックを割り当てる必要があります。

MySQLでは、操作はトランザクションをブロックしながらデータを新しいテーブルにコピーします。これは、v。5.6より前のMySQL DBAの主な痛みでした。

良いニュースは、MySQL 5.6リリース以降、制限が ほとんど解除された になり、MYSQL DBの真の力を享受できるようになったことです。

1

_pt-online-schema-change。私はこのツールを使用して、複数のスレーブを持つAWS RDSで移行を行ってきましたが、非常にうまく機能しました。私はあなたに役立つかもしれないそれを行う方法についての精巧なブログ記事を書きました。

ブログ: http://mrafayaleem.com/2016/02/08/live-mysql-schema-changes-with-percona/

1
Rafay

まだこれを読んでいる人やここに来た人は、mongodbのようなNoSQLデータベースシステムを使用する大きな利点です。テーブルを変更して、追加の機能用の列を追加するか、数百万の行と大量の書き込みを行う大きなテーブルにインデックスを追加することと同じ問題がありました。最終的にロックが非常に長くなるため、LIVEデータベースでこれを行うと、ユーザーがイライラします。小さなテーブルでは、それで済ますことができます。

「テーブルを変更しないように設計する」必要があるという事実は嫌いです。私はそれが今日のウェブサイトの世界でうまくいくとは思わない。ユーザーがソフトウェアをどのように使用するかを予測することはできません。そのため、ユーザーのフィードバックに基づいて物事を急速に変更します。 mongodbを使用すると、ダウンタイムなしで「列」を自由に追加できます。実際にはそれらを追加することもせず、新しい列にデータを挿入するだけで、自動的に実行されます。

チェックアウトに値する:www.mongodb.com

1
Brian Gruber

一般的に、答えは「いいえ」になります。多くの更新を必要とする可能性があるテーブルの構造を変更しています」と私は間違いなくそれに同意します。これを頻繁に行うことを期待する場合は、「ダミー」列の代わりを提供します-代わりにVIEWsを使用しますSELECTingデータのテーブルのIIRC、ビューの定義の変更は比較的軽量であり、クエリプランのコンパイル時にビューを介した間接化が行われます。費用は、新しいテーブルに列を追加し、列のJOINを表示します。

もちろん、これは、外部キーを使用して削除などのカスケードを実行できる場合にのみ機能します。もう1つのボーナスは、データの組み合わせを含む新しいテーブルを作成し、クライアントの使用を妨げることなくビューをポイントできることです。

ちょっとした考え。

1
D.Shawley

次の2つの方法のいずれかをお勧めします。

  1. 潜在的な変更を念頭に置いてデータベーステーブルを設計します。たとえば、私はコンテンツ管理システムと連携しており、コンテンツのデータフィールドを定期的に変更しています。最初のCMSフィールド要件に一致するように物理データベース構造を構築する代わりに、柔軟な構造を構築することをお勧めします。この場合、blobテキストフィールド(たとえば、varchar(max))を使用して柔軟なXMLデータを保持します。これにより、構造的な変更が非常に少なくなります。構造の変更にはコストがかかる可能性があるため、ここでもコストのメリットがあります。

  2. システムのメンテナンス時間がある。変更中にシステムがオフラインになり(毎月など)、その日のトラフィック量が最も少ない時間(午前3時から5時など)に変更がスケジュールされます。変更は実稼働のロールアウトの前にステージングされるため、ダウンタイムの適切な固定ウィンドウ推定値が得られます。

2a。システムにダウンタイムが発生したときにサイト全体がダウンしないように、冗長サーバーを用意します。これにより、サイト全体をダウンさせることなく、更新をずらして「ロール」できます。

オプション2および2aは実行不可能な場合があります。彼らは大規模なサイト/操作のみになりがちです。ただし、これらは有効なオプションであり、ここで紹介するオプションはすべて個人的に使用しました。

1
pearcewg

SeanDowneyが述べたように、pt-online-schema-changeは、ここで質問で説明したことを実行するための最良のツールの1つです。最近、ライブDBで多くのスキーマを変更しましたが、かなりうまくいきました。詳細については、私のブログ投稿でこちらをご覧ください: http://mrafayaleem.com/2016/02/08/live-mysql-schema-changes-with-percona/

1
Rafay

ダミーの列は、その型を予測できる場合(およびNULL可能にする場合)に適しています。ストレージエンジンがnullを処理する方法を確認してください。

MyISAMは、電話で空港を通過する際にテーブル名に言及した場合でもすべてをロックします。それだけで...

とはいえ、ロックはそれほど大きな問題ではありません。すべての行に新しい列のデフォルト値を追加しようとしていないが、nullのままにし、ストレージエンジンが書き込みを行わないほどスマートである限り、ロックのみで大丈夫ですメタデータを更新するのに十分な時間保持されました。新しい値を書き込もうとすると、あなたは乾杯します。

0
SquareCog

TokuDBは列を追加/ドロップし、インデックスを「ホット」に追加できます。テーブルはプロセス全体で完全に利用可能です。 www.tokutek.comから入手できます

0
tmcallaghan