ALTER TABLE などのDDLを実行するとき、MySQLは内部的に3種類のアルゴリズムのいずれかでテーブルを変更します。
どのアルゴリズムが使われるかによって、実行時間・ロックの有無・本番への影響(ストレージ容量や負荷)が大きく変わります。
本番環境で安全にスキーマ変更を行うために、各アルゴリズムの挙動と使い分けを覚書きも兼ねて整理しました。
MySQLのDDLアルゴリズム | 大きく分けて3つ
1. INSTANT
MySQL 8.0より追加されたアルゴリズムです。
テーブルの実データには一切触れず、メタデータだけを書き換えます。
特徴
- 実行がほぼ一瞬(ミリ秒単位)で完了する
- ユーザーが体感するようなテーブルロックは発生しない(DDL開始時と終了時にMetadata Lockが取得される)
- 使える操作が限られる
使用シーン
- カラムの追加(最も代表的なユースケース)
- MySQL 8.0.29以降:カラムの削除、およびテーブルの先頭や途中へのカラム追加・挿入
補足
INSTANTが高速な理由は、カラム追加がこれから入ってくるデータへのルール追加であり、既存の行を変更する必要がないためです。
MySQLはこのカラムが存在しない既存行はデフォルト値として扱うという形でメタデータだけ書き換えれば済みます。
ALTER TABLE `orders`
ADD COLUMN `is_gift` TINYINT(1) NOT NULL DEFAULT 0,
ALGORITHM=INSTANT;
2. INPLACE
テーブルを別名で丸ごと新規作成して入れ替える(COPY)のではなく、可能な限り既存のテーブル構造をその場で変更しようとするアルゴリズム。
LOCK オプションなどと組み合わせて使用します。
特徴
- 完全に「コピーなし」とは限らない(後述のテーブルリビルドが発生する場合がある)
- ロックの種類を LOCK=NONE / LOCK=SHARED / LOCK=EXCLUSIVE で明示的に制御できる
- 既存データをスキャンしたり、インデックスを再構築したりする操作に使用する
⚠️ 注意:テーブルリビルド(再構築)について
INPLACEには「メタデータのみ書き換える処理」「インデックスのみ書き換える処理」のほかに、内部でデータを新しいテーブル構造に詰め替える処理(テーブルリビルド)を伴うものがあります(例:PRIMARY KEYの追加など)。
リビルドが発生する場合、一時的に元のテーブルとほぼ同等のディスク空き容量が必要になるため、ストレージ残量に注意が必要です。
ロックの種類
| LOCK指定 | 読み取り | 書き込み | 主な用途・挙動 |
|---|---|---|---|
| LOCK=NONE | ○ | ○ | インデックス追加など(オンラインDDL) |
| LOCK=SHARED | ○ | ✗ | PRIMARY KEY追加など |
| LOCK=EXCLUSIVE | ✗ | ✗ | DDL実行中、全アクセスをブロック(基本使わないので以後の説明も割愛) |
使用シーン(LOCK=NONE)
- セカンダリインデックスの追加・削除
- 外部キー(FK)の追加(条件による)
- カラム名の変更
使用シーン(LOCK=SHARED)
- PRIMARY KEYの追加
- フルテキストインデックスの追加
-- LOCK=NONE の例(インデックス追加)
ALTER TABLE `orders`
ADD INDEX `orders_shop_id_created_at_index` (`shop_id`, `created_at`),
ALGORITHM=INPLACE, LOCK=NONE;
-- LOCK=SHARED の例(PRIMARY KEY追加)
ALTER TABLE `logs`
ADD PRIMARY KEY (`log_id`),
ALGORITHM=INPLACE, LOCK=SHARED;
なお、INPLACEだから安全とは限りません。
INPLACEでもテーブルリビルドが発生する操作では、大量のI/Oや一時領域を消費するため、本番適用前には必ずステージング環境などで検証しましょう。
3. COPY
最も古典的なアルゴリズムで、テーブルを丸ごと裏側でコピーして変更を適用します。
COPYの実行順序は、
- 変更後の新しい構造のテンポラリテーブルを作成する
- 既存テーブルの全行を新テーブルにコピーする
- コピー完了後、新旧テーブルを入れ替えて古い方を削除する
の流れで行われます。
特徴
- テーブルサイズ(レコード数)が大きいほど実行時間が長くなる
- 少なくとも書き込み(INSERT / UPDATE / DELETE)はブロックされる
- どんな変更にも対応できる汎用的なフォールバック用アルゴリズム
使用シーン
- 大幅なデータ型の変更(例:INT → BIGINT)
- 互換性のない文字列型の縮小や、境界をまたぐ拡張
- INPLACEやINSTANTが利用できない変更
補足事項
本番の大規模テーブルに対してCOPYが走ると、ALTER実行中ずっとアプリケーションからの書き込みが止まり、サービス停止に追い込まれる可能性があるため、可能な限り回避すべき。
INPLACEやINSTANTがどうしても使用できない場合の最終手段といった感じです。
MySQLのDDLアルゴリズムの使い分けケーススタディ
ケース1:新しいカラムを追加する場合 → INSTANT
カラム追加(MySQL 8.0.29以降であれば削除も)はINSTANTが使えます。
実行が一瞬で完了し、本番テーブルへの影響を最小化できます。
ALTER TABLE `items`
ADD COLUMN `is_draft` TINYINT(1) NOT NULL DEFAULT 0,
ALGORITHM=INSTANT;
ケース2:インデックスの追加・貼り替えの場合 → INPLACE, LOCK=NONE
インデックス追加は既存の全データを読んでBツリー構造を構築する物理的な作業が必要なため、INSTANTは使えません。
しかし、INPLACEに LOCK=NONE を組み合わせることで、バックグラウンドでインデックスを構築しつつ、アプリケーションからの書き込みを継続できます。
なお、DDL開始時・終了時にはMetadata Lockが取得されるため、長時間トランザクションが存在するとALTER開始が待たされる場合があります。
ALTER TABLE `orders`
ADD INDEX `orders_shop_id_created_at_index` (`shop_id`, `created_at`),
ALGORITHM=INPLACE, LOCK=NONE;
ケース3:外部キー(FK)を追加する場合 → INPLACE, LOCK=NONE(条件次第)
外部キー追加は環境や条件によって挙動が変わります。
既存データの整合性確認が必要になるため、場合によってはLOCK=NONEが利用できないことも。
大量データに対して外部キーを追加する場合は、事前検証を推奨します。
-- ALTER実行時の既存行の整合性チェックをスキップ(FK追加に INPLACEを使えるようにする)
SET foreign_key_checks = 0;
ALTER TABLE `orders`
ADD CONSTRAINT `orders_shop_id_foreign`
FOREIGN KEY (`shop_id`) REFERENCES `shops` (`shop_id`),
ALGORITHM=INPLACE, LOCK=NONE;
-- ALTER実行時の既存行の整合性チェックを有効に戻す
SET foreign_key_checks = 1;
⚠️ 注意点
外部キー追加時は、既存データに親子関係の不整合が存在するとDDL自体が失敗する可能性があります。
事前に整合性チェックを行ってから実施することをおすすめします。
ケース4:PRIMARY KEYを追加する場合 → INPLACE, LOCK=SHARED
PRIMARY KEYの追加は、全行をスキャンしてテーブルの物理配置(クラスタインデックス)そのものを再構築する必要があります。
一般的にはLOCK=NONEは利用できず、LOCK=SHARED相当の制約が発生します。
ALTER TABLE `logs`
ADD PRIMARY KEY (`log_id`),
ALGORITHM=INPLACE, LOCK=SHARED;
書き込みはブロックされますが、COPYアルゴリズムと比べて内部的な最適化が効くため、実行時間を短縮できます。
ただし、データ量に応じたテーブルリビルドの時間とディスク空き容量が必要です。
ケース5:カラムのデータ型を変更する場合 → 基本はCOPY
INT → BIGINT のようなデータ型変更は、データ自体のビット幅が変わるため、既存データの再構築が必要となり、COPYアルゴリズムまたはINPLACEによる重いリビルドになります。
💡 例外:VARCHAR の長さ拡張
VARCHAR(50) から VARCHAR(255) への拡張など、「文字長を記録する領域のサイズが変わらない範囲」での拡張であれば、MySQL 8.0ではINPLACEで実行可能な場合があります。
逆に、
- VARCHAR(255) → VARCHAR(50)
のような変更は、既存データの再構築が必要になる場合があります。
型変更を本番の大規模テーブルに対して実行する場合は、メンテナンス時間を確保するか、pt-online-schema-change や gh-ost などの外部ツールの使用を検討するのが現実的です。
運用上のTips:ALGORITHM と LOCK を明示するメリット
SQL文に ALGORITHM=INPLACE, LOCK=NONE などを明示的につけての実行には、大きなメリットがあります。
もし指定したアルゴリズムやロック条件をMySQLが満たせない場合、MySQLは自動で重いアルゴリズムにフォールバックせず、実行前にエラーとして返します。
これにより、
- 気づかないうちに本番環境でCOPYが走り、テーブルが長時間ロックされてしまった
という事故を未然に防ぐことができます。
まとめ
| アルゴリズム | LOCK指定 | 実データへの操作 | 書き込みブロック | 主な用途 |
|---|---|---|---|---|
| INSTANT | なし | なし(メタデータのみ) | なし | カラム追加(8.0.29〜は削除も) |
| INPLACE | LOCK=NONE | あり | なし | インデックス追加など |
| INPLACE | LOCK=SHARED | あり(リビルドあり) | あり | PRIMARY KEY追加 |
| COPY | 指定不可 | あり(全行コピー) | あり | 互換性のない型変更など |
本番運用における優先順位は、
INSTANT > INPLACE(LOCK=NONE) > INPLACE(LOCK=SHARED) > COPY
の順で、COPYやリビルドを伴うSHAREDはできる限り避けることが望ましいです。
難しい場合は、アプリケーションをメンテナンスモードにして実施するようにしましょう。
また、MySQLのバージョンや具体的な操作内容によって挙動は変わります。
本番適用前には必ずステージング環境でALTER TABLEを実行し、実際の挙動や実行時間を確認してから適用しましょう。