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


107

MySQLでALTER TABLEステートメントを実行すると、ステートメントの期間中、テーブル全体が読み取りロックされます(同時読み取りは許可されますが、同時書き込みは禁止されます)。大きなテーブルの場合、INSERTまたはUPDATEステートメントが長時間ブロックされる可能性があります。テーブルがプロセス全体でまだ更新可能であるような方法で列を追加するような「ホットオルター」を行う方法はありますか?

ほとんどの場合、MySQLのソリューションに興味がありますが、MySQLで解決できない場合は、他のRDBMSにも興味があります。

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


2
テーブルを何回変更するのか不思議に思わなければなりませんか?
Allain Lalonde、

1
私見、データベーススキーマの変更はまったく新しいバージョンに関連付けられています。他の変更のように散発的に展開されることはありません。それは必然的に大きな問題です。
dkretz 2009年

9
@AllainLalonde-特にシステムのダウンタイムにより人命や多額の費用がかかる場合は、0回を超えるとこの質問が正当になります。そしていずれにせよ、新しいソフトウェア要件が時々現れます。
Nathan Long

回答:


60

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

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

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

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

編集:

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

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

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

2
交換する前に、綿密なテスト計画を立ててください。失敗した場合は、最初からやり直してください。
dkretz 2009年

2
トリガーを介して同期を管理することは素晴らしいアイデアでした。MySQLを長い間使用しているので、トリガーがあることを忘れてしまいます。私はこの手法を使用しましたが、機能するホット・オルター・スクリプトができました。プログレスバー付き。そして、それはMyISAMで動作します。人生は素晴らしい。
ダニエル

2
+1これは、文字通り、SQL Enterprise ManagerがUIで特定の種類のテーブル変更を行う場合に舞台裏で行うことです。SQL 2008では、ユーザーが実際に警告を追加して、ユーザーがこの抜本的なアクションの実行を認識できるようにしました。
BradC 2009年

2
変更されるテーブルを参照する外部キーについては何も言及していません。それは問題ではないでしょうか?
ラファイ

2
@MohammadRafayAleem-AUTOINCREMENTフィールド、ビュー、トリガーなど。ただし、それでも、アプローチは引き続き機能します。
MatBailie 2016

42

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

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

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

彼らのツールの中には、学習曲線、つまりデータベースへの接続を備えているものもありますが、いったんそれが下がると、それらはすばらしいツールになります。

例:

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

リンクが壊れているようです。このリンクは機能していることがわかりました。
Noam Ben Ari

25

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を参照してください。

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

あるいは、ALTER ONLINE TABLEを使用して、ALTER TABLEが並行操作をブロックしない(ロックを取得しない)ことを確認できます。LOCK = NONEと同等です。

MariaDB KB、ALTER TABLEについて


3
MySQLの現在のバージョンを参照しなくなったため、他のすべての回答を純粋に否定することを考えると、これを投票で投票する以外に方法がないのは残念です。
Burhan Ali


14

可能であれば、Postgresをお勧めします。postgresを使用すると、次の手順で本質的にダウンタイムは発生しません。

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

これは少し前に書いたものですが、おそらく他のメリットについての洞察をさらに深めることができます。


6
Postgresは、変更に対して排他ロックを作成し、他の人がそのテーブルから読み取るのを防ぎます。
clofresh 2011

5
「本質的にダウンタイムなし」のビットには同意しません。clofreshが言ったように、ALTER TABLEはテーブルの排他ロックを取得して、すべての同時読み取りと書き込みをブロックします。私の経験では、アクティブなテーブルの場合、ほとんどの場合、ロックは取得されません(ALTER TABLEは飢餓状態になります)。また、トランザクションでは、細心の注意を払わなければ、簡単にデッドロックが発生する可能性があります。そのため、Postgresで既存のテーブルを変更するときに、常にダウンタイムを設定します。
Pankrat 2012

1
より詳細な説明:dba.stackexchange.com/questions/27153/…排他ロックの影響とそれを回避するいくつかの方法について言及しています
John Douthat

4
はい、postgresのテーブルを変更すると排他ロックが適用されますが、操作自体がミリ秒単位で完了するため、ほとんどの場合、これは事実上無関係です。個人的に、営業日の真ん中に1億行のテーブルに列を追加しましたが、結果としてダウンタイムは発生しません。
Noah Yetter 2014

2
@cobbzillaはい、DROP COLUMNも同じくらい高速です。内部的には、基本的には列を非表示としてマークします。削除される前にその列に存在していた値はまだデータファイルにあり(他のトランザクションからも見える)、VACUUM FULLを実行しない限り、そのまま残ります。
Noah Yetter

7

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

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

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

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

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

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

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

これはすべてテストするのが非常に面倒なので、メジャーバージョンをリリースするときはいつでも、日曜日の早朝のサービス停止があります。


3

アプリケーションの更新を行うときにデータベースのダウンタイムが許されない場合は、高可用性のために2ノードのクラスターを維持することを検討してください。シンプルなレプリケーション設定で、提案するようなほぼ完全にオンラインの構造変更を行うことができます。

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

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


1
古いマスターはクラスターの外にありますか、クラスター内にありますか?
John Chornelius

2

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

これは非常に大きな変更であり、DBMSがそれをサポートすることはないと思います。そもそも表のデータでそれを実行できることは利点と考えられています。



そうです、MySQLは異常です。そのため、「標準」テーブルについて具体的にしました。
dkretz 2009年

あなたが書いた-標準のMySQLテーブルはテーブルロックのみを行う-これは正しくない。
エランガルペリン

引用したページのMyISAM(つまりMySQL標準)テーブルについてこれをどのように解釈しますか?「MySQLは、MyISAMテーブルとMEMORYテーブルにはテーブルレベルのロック、BDBテーブルにはページレベルのロック、InnoDBテーブルには行レベルのロックを使用します。」
dkretz 2009年

ストレージエンジンには、行レベルのロックを使用するものと、テーブルレベルのロックを使用するものがあります。標準のストレージエンジンはありません(おそらくphpMyAdminのデフォルトを意味しているかもしれません...)
Eran Galperin

2

一時的な解決策...

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

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

ダウンタイムが発生したら、元のテーブルを変更し、DMLクエリを変更して、以前に作成した新しいテーブルを削除できます

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


1

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

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

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


1

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

  1. 潜在的な変更を考慮してデータベーステーブルを設計します。たとえば、コンテンツのデータフィールドを定期的に変更するコンテンツ管理システムを使用してきました。物理的なデータベース構造を構築して初期のCMSフィールド要件に合わせるのではなく、柔軟な構造で構築する方がはるかに優れています。この場合、BLOBテキストフィールド(varchar(max)など)を使用して柔軟なXMLデータを保持します。これにより、構造変更の頻度が非常に少なくなります。構造変更はコストがかかる可能性があるため、ここにもコストをかけるメリットがあります。

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

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

オプション2と2aは実行できない場合があります。それらは大規模なサイト/操作のみを対象とする傾向があります。ただし、これらは有効なオプションであり、ここに示すオプションはすべて個人的に使用しました。


1

誰かがまだこれを読んでいるか、ここに来た場合、これはmongodbのようなNoSQLデータベースシステムを使用する大きな利点です。テーブルを変更して追加の機能の列を追加するか、数百万行の書き込みが多い大きなテーブルにインデックスを追加するかについても、同じ問題がありました。これは非常に長い時間ロックされることになるため、LIVEデータベースでこれを行うと、ユーザーに不満が生じます。小さなテーブルでは、それを回避できます。

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

チェックアウトする価値がある:www.mongodb.com


2
MySQLは依然として多くのシステムで使用されているため、私も熱心なNoSQLサポーターであるにもかかわらず、問題はSQL RDBMSでスキーマを変更する方法についてです。
Alexy、2011

1

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

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

ちょっとした考え。


1

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

MySQLでは、トランザクションがブロックされている間、操作によってデータが新しいテーブルにコピーされます。これは、v。5.6より前のMySQL DBAにとって主な問題でした。

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


3
MySql 5.6の変更に関する参照にリンクしようとしていたようですが、機能しませんでした。もう一度やり直してください。
dg99、2015年


1

あなたは間違いなく試みるべきpt-online-schema-changeです。私はこのツールを使用して、複数のスレーブを持つAWS RDSでの移行を実行してきましたが、私にとっては非常にうまく機能しました。私はあなたに役立つかもしれないそれを行う方法についての精巧なブログ投稿を書きました。

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


0

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

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

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


1
InnoDBテーブルにNULL列を追加しようとすると、テーブル全体を再構築する必要がありました。単純な「メタデータの更新」操作ではありません。
ダニエル

新しい機能が必要な場合は、使用を開始するだけで新しい列を「追加」できるように、データベースの設計時にデータベースに追加のnull許容列を含めることを考えていました。わかりやすい名前は付けられませんが、データ型が正しく選択/予測されていれば機能するはずです。
スーパーキャット2018年

0

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


-6

あんまり。

結局のところ、テーブルの基になる構造を変更しているのですが、これは、基になるシステムにとって非常に重要な情報です。また、ディスク上の多くのデータを(おそらく)移動しています。

これを頻繁に行う予定の場合は、将来使用できる「ダミー」列をテーブルに埋め込むだけのほうがよいでしょう。


3
テーブルにダミーの列を埋め込むのは非常に悪い考えのようです。
ジョスト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.