MySQLでオンラインでDDLを実行する際の注意点について

目次

はじめに

過去の古いMySQLのバージョンでは、テーブル定義の変更を伴うようなDDLを実行する際には、元のテーブルとは別に新しいテーブルを作成し、元のテーブルデータを新テーブルにコピーした後に元のテーブルと入れ替えるといった手法で行われていました。

また、その間の対象テーブルへの更新はブロックされるといった挙動であった為、アプリケーションサービスを稼働中にDDLを実行するというのが、かなり敷居が高いものでした。

それが、MySQL5.5 より Fast Index Creation が採用され、InnoDBのセカンダリインデックスの作成、削除にはテーブルコピーを伴わないで実行できるようになり、処理の高速化が図られています。

その後、MySQL5.6 ではオンラインDDLが使用できるようになり一部のDDLでは、テーブルコピーを伴わずに、更新もブロックしないということが可能となり、更に MySQL 8.0.12 からは、メタデータの更新のみで処理が完了するようなDDLも存在しています。

メタデータの更新のみを行う INSTANT DDL については弊社の以下のブログでも紹介しておりますので、ご一読頂ければと思います。

そんな中、今回は、アプリケーションサービスを稼働中にDDLを実行する際に考慮が必要になる、以下の点をピックアップして記載したいと思います。

  • 実行に伴うロック待ちの考慮
  • 実行に要する時間に対する考慮
  • 実行中のテーブルデータの更新量に関する考慮

前提知識

まず、ALTER TABLE には ALGORITHM 指定をする事が可能で、DDLを実行するアルゴリズムとして、以下3つの指定が可能となっています。

ALGORITHM 概要
INSTANT データディクショナリのメタデータのみを変更する。
INPLACE テーブル定義をインプレースで変更する。一部のDDLはテーブルの再構築を伴う。
COPY 新たにテーブルを作成し、既存のテーブルからデータをコピーした後に、テーブルを入れ替える。

上記表の下に記載されているもの程、サービス影響は高く、DDLによって指定できる ALGORITHM は異なりますが、特に指定のなかった場合、もしくは DEFAULT を指定した場合は、上から順に操作可能なものを評価して選択されます。

こちらについての詳細は、以下のリファレンスをご確認下さい。

また、ALTER TABLE では LOCK 指定も可能で、DDL実行中にDMLへの操作を制御することが可能です。

LOCK 概要
NONE 実行中に対象テーブルの読み取りと書き込みを許可する。
SHARED 実行中に対象テーブルの読み取りは許可されるが、書き込みはブロックする。
EXCLUSIVE 実行中に対象テーブルの読み取りと書き込みもブロックする。

こちらも、上記表の下に記載されているもの程、サービス影響は高く、DDLによって指定できる LOCK は異なりますが、特に指定のなかった場合、もしくは DEFAULT を指定した場合は、上から順に操作可能なものを評価して選択されます。

こちらについての詳細は、以下のリファレンスをご確認下さい。

実行に伴うロック待ちの考慮

メタデータロック(DDL実行フェーズ)

どんなDDLを実行するにしても、メタデータロック待ちが発生する可能性があることを考慮する必要があります。

通常のDML操作でテーブル参照を行う場合に取得するメタデータロックは、共有ロックとなりますが、DDL操作では排他ロックを取得する為、共有ロックと排他ロックでロック競合が発生しえます。

以下で一例を見てみます。
まずは、あるセッション内でトランザクションを開始し、t1テーブル をSELECTします。

[Session.1]

その後、別のセッションで、 t1テーブル のカラムからデフォルト値を削除します。

[Session.2]

デフォルト値の削除は、以下のリファレンスの Dropping the column default value に該当し、Instant:YesPermits Concurrent DML:Yes である為、ロック待ちも発生せずに瞬時に処理が終了するのかと思いきや、しばらく経ってもDDLは終了しません。

そこで、SHOW PROCESSLIST; を確認してみると、DDLの実行は Waiting for table metadata lock となっており、メタデータロック待ちとなっていることが分かります。

この状態で、別のセッションで t1テーブル をSELECTしてみると、こちらもレスポンスが返されません。

[Session.3]

再度、SHOW PROCESSLIST; を確認してみると、[Session.3]で実行したSELECTも Waiting for table metadata lock となっており、メタデータロック待ちとなっていることが分かります。

これは、[Session.1] のSELECTで t1テーブル の共有ロックが取得され、その後、 [Session.2] のDDLが排他ロックを取得しようとして、メタデータロック待ちとなります。
この状態になると、排他ロックの取得待ちとなっている [Session.2] が存在することで、[Session.3]のSELECTや、その他の t1テーブル を参照するDMLは全て共有ロックが取得できずにロック待ち状態となります。

なので、DDLをサービス停止を伴わないで実行する際には、長きに渡って実行されているトランザクションがないかを確認し、DDLを実行するセッションでは以下のように lock_wait_timeout(デフォルト:31536000秒=365日) を短めに設定してからDDLを実行することが安全策と言えます。

これにより、排他ロックを取得するDDLがロック待ち状態になったとしても5秒経過するとタイムアウトエラーとなる為、他のセッションで共有ロックを取得するDMLが実行された場合には、最大5秒のロック待ちで済むことになります。

メタデータロックによる問題が発生した場合の対処法については、以下のブログにも記事を投稿しておりますので、ご確認頂ければと思います。

メタデータロック(DDLコミットフェーズ)

上でご紹介したのは、以下のリファレンスの Phase 2: Execution に記載されているメタデータロックについてとなります。

例で挙げたように、DDLを実行する前段階で瞬間的に排他ロックを取得する為、この時点で既に共有ロックを取得していたトランザクションが終了していなければ、ロック待ちになるというケースとなります。

加えて、同リファレンスの Phase 3: Commit Table Definition に記載されているように、DDLの実行の終了段階となるコミットフェーズでも、排他ロックを取得する為、DDLの実行時にメタデータロック待ちが発生しなくても、終了するまでに対象テーブルを参照したトランザクションが終了していなければ、DDLの実行は最後にメタデータロック待ちとなります。

先程の例で言うと、先に [Session.2] でDDLを実行し、これが終了するまでに、[Session.1] のような、対象テーブルを参照してトランザクションが終了していない状態のセッションが存在した場合、DDLの実行は終了せずにメタデータロック待ちとなります。

DDL実行中のロック

以下のリファレンスの表の Permits Concurrent DMLNo と記載されているDDLについては、同時DMLが許可されておらず、LOCK=SHARED となる為、DDLが実行されている間の対象テーブルへの更新はロック待ちとなることを認識しておく必要があります。

さらに、Permits Concurrent DMLYes* のものも条件によっては、LOCK=SHARED になることがあり得るということと、 Permits Concurrent DMLYes のものでも上記リファレンスの表外の詳細説明で ALGORITHM=COPY となるケースが記載されていたりする為、これらも LOCK=SHARED となります。

なので、DDL実行中に更新も含めたDMLが実行可能であるかどうかは、検証環境等で同じテーブル構造を持つテーブルに対して LOCK=NONE を指定してDDLを実行し、エラーとならないかを確認してみるのがよいでしょう。

更新をブロックしないと実行できないケースでは、以下のようなエラーになります。

実行に要する時間に対する考慮

以下のリファレンスの表の In PlaceNo と記載されているDDLについては、Copy形式になるもので、これはテーブルデータ量に応じて実行に時間を要します。

In PlaceYes であっても Rebuilds TableYes でテーブル再構築を伴うものもあり、 このようなDDLもテーブルデータ量に応じて実行に時間を要します。

他にも、In PlaceYesRebuilds TableNo でも、同様に時間がかかることがあります。

例えば、Creating or adding a secondary index はセカンダリインデックスの作成になりますが、In PlaceYes かつ Rebuilds TableNo となりますが、テーブルのデータ量に応じて実行時間がかかることが考えられます。

つまりは、InstantYes 以外のDDLについては、実行に時間がかかる可能性がある為、DDLの実行にかかる時間を事前に把握したい場合は、同じテーブル定義及びデータ量を持ち、かつ本番環境と同レベルのサーバリソースとなる検証環境などで、事前に確認する必要があります。

セカンダリインデックスの作成または再構築を伴うDDLの実行時間を改善したい場合、CPU負荷及びディスクI/Oがボトルネックになっていなければ、以下のパラメータを調整することで改善する見込みがありますので、調整してみてもよいでしょう。

  • innodb_parallel_read_threads

    • クラスター化インデックスのスキャンに使用できるスレッド数
  • innodb_ddl_threads

    • 一時ソートファイルのデータの並び替え及びセカンダリインデックスにソートされたデータをロードするスレッド数

もしも、セカンダリインデックスの作成または再構築を伴うDDLの実行中にメモリ不足となるようなケースでは、以下のパラメータのバッファサイズを制限することで問題を回避できる可能性があります。

これらに関する詳細は、以下のリファレンスをご確認下さい。

※ 上記のそれぞれのパラメータは以下のバージョンで追加されています。

  • innodb_parallel_read_threads (MySQL 8.0.14)
  • innodb_ddl_threads, innodb_ddl_buffer_size (MySQL 8.0.27)

実行に時間がかかるDDLは、基本的にCPU負荷及びディスクI/Oの負荷が高まる為、その間のアプリケーションが発行するDMLのレスポンスが遅くなってしまう可能性もあります。
また、レプリケーション構成でDDLを実行した場合、ロック待ち以外の時間でソースサーバで時間がかかるということは、レプリカサーバでも同様に時間がかかる為、レプリケーション遅延が大きく発生してしまう可能性もあります。

サービスが稼働している状態で、DDLを実行するということは、ロック待ち以外にも影響を及ぼす可能性がある点も考慮すべき点かと思います。

但し、レプリケーション遅延やリソース負荷に関する問題に関しては、以下の製品を用いることで、回避できる可能性がある為、検証してみるとよいかもしれません。

実行中のテーブルデータの更新量に関する考慮

DDLの実行中に更新が可能なケースでは、実行中の該当テーブルへの更新情報を別のログファイルに書き出し、テーブル定義を変更した後に、その更新情報を適用するといった動作になります。

その為、サービスが稼働している状態でDDLの実行に時間がかかる場合、ログファイルのサイズが大きくなり、更新情報の適用量が多くなることにより、DDLの実行時間が長くなるといったことも考慮する必要があります。

加えて、更新情報を記録するログファイルサイズには制限があり、innodb_online_alter_log_max_size の制限を超えて更新が発生した場合、更新情報をテーブルスペースファイルに適用する段階で、DDLの実行がエラー終了することとなります。

それでは挙動を確認してみます。
まずは、検証前に innodb_online_alter_log_max_size を最小値となる64KBに変更しておきます。

設定値変更後に、以下の sysbench で5千万件のレコードを持つテーブルを1つ作成しました。

その後、以下のコマンドで、このテーブルに更新負荷をかけるように oltp_write_only シナリオを実行します。

続いて、対象テーブルにインデックスを追加する ALTER文 を実行します。

25分程の時間が経過した時点で、上記、ALTER文 はエラーとなりました。

実行に時間のかかる DDL が終了段階に近づいているにも関わらず、このような形でエラーになってしまう残念な結果となってしまう為、サービスの稼働中に、DDLを実行する対象テーブルに多くの更新が入ることが予想される場合は、innodb_online_alter_log_max_size を事前に調整するようにしましょう。

また、他によく知られている例として ユニークインデックス を追加中に、対象インデックス項目に、既存のテーブルデータと重複するレコードの登録があった場合も同様です。
テーブル定義を変更した後に、更新情報をテーブルスペースファイルに適用する段階で、DDLの実行がエラー終了することになりますので、プライマリキーやユニークキーを追加する際は、注意が必要です。

まとめ

今回は、アプリケーションサービスを稼働している中で、DDLを実行する際の注意点について記載しました。
可能な限り、メンテナンス期間を設けてDDLを実行するのが望ましいのですが、メンテナンス期間を設けられないようなサービスもあるかと思います。

しかし、実行中に更新を許可する ALGORITHM=INPLACE でかつ LOCK=NONE であるオンラインDDLには、以下のようなバグが存在しています。

上記バグについては、DDL実行中に予期せず行が削除されるというものと、プライマリキーやユニークキーの重複が発生していないのにも関わらず、重複キーエラーでDDLの実行がエラーとなるといったものです。
こちらは、LTSとなるMySQL8.0、MySQL8.4では、執筆時点(2024/12/6)では修正されておらず、以下のバージョンが影響を受けます。

  • MySQL8.0:MySQL8.0.27以降のバージョン
  • MySQL8.4:MySQL8.4.0以降のバージョン

このような問題を避けたい場合は、DDL実行中の更新がブロックされてしまいますが、 ALGORITHM=COPY で実行するか、前で挙げた、以下のような製品を使用してみることをお勧めします。

スマートスタイルTECHブログについて

スマートスタイルTECHブログでは、日頃MySQLのサポート業務に従事している有資格者で構成された技術サポートチームがMySQLに関する技術情報を発信しています。データベースのお困りごとはお気軽にご相談下さい。

よかったらシェアしてね!
  • URLをコピーしました!
目次