AI Session Notes - 2026-03-23
MySQL Online DDL の実行時間予測と実行戦略
学んだこと
- EXPLAIN は DML(SELECT/INSERT/UPDATE/DELETE)専用であり、DDL(ALTER TABLE 等)には使えない
- Online DDL の実行時間は、テーブルサイズ・DDL の種類・I/O 性能・同時ワークロード・インデックス数など複数要因に依存するため、正確な事前予測は困難
- 実行時間の見積もりには、ステージング環境での実測が最も信頼性が高い
詳細
DDL アルゴリズムの3分類(MySQL)
| アルゴリズム |
挙動 |
速度 |
例 |
| INSTANT(MySQL 8.0+) |
メタデータのみ変更 |
ミリ秒単位 |
カラム末尾追加、デフォルト値変更 |
| INPLACE |
テーブル再構築するが DML 非ブロック |
データ量に比例 |
インデックス追加、一部の型変更 |
| COPY |
新テーブルにコピー→差し替え |
最も遅い |
互換性のない型変更、文字コード変更 |
実行時間の見積もり方法
information_schema.tables でテーブルサイズ(行数・データサイズ・インデックスサイズ)を確認
- ステージング環境で同等データ量のテーブルに対して ALTER を実測
pt-online-schema-change(Percona)や gh-ost(GitHub)を使えば、進捗表示と推定残り時間が得られる
performance_schema.events_stages_current で ALTER 実行中の進捗をリアルタイム確認可能
-- 進捗モニタリング
SELECT
EVENT_NAME,
WORK_COMPLETED,
WORK_ESTIMATED,
ROUND(WORK_COMPLETED / WORK_ESTIMATED * 100, 1) AS pct
FROM performance_schema.events_stages_current
WHERE EVENT_NAME LIKE '%alter%';
中規模テーブル(数百万行)への複合インデックス追加戦略
学んだこと
- 500万行程度のテーブルへのインデックス追加は、素の Online DDL(
ALGORITHM=INPLACE, LOCK=NONE)で数分以内に完了するケースが多い
ALGORITHM=INPLACE, LOCK=NONE を明示的に指定すると、INPLACE 不可時にエラーで停止してくれるため安全
pt-online-schema-change や gh-ost は数千万〜億行規模や、書き込みが常に多い環境で本領を発揮する
- 複合インデックスのカラム順は、WHERE 句の等値条件カラムを先頭に、ORDER BY カラムを末尾に配置するのが基本
詳細
-- 明示的にアルゴリズムとロックを指定して安全に実行
ALTER TABLE your_table
ADD INDEX idx_col1_col2 (col1, col2),
ALGORITHM=INPLACE,
LOCK=NONE;
実行前の確認事項:
information_schema.tables でテーブルサイズと既存インデックス数を確認
information_schema.innodb_trx で長時間実行中のトランザクションがないことを確認
- 可能ならトラフィックの少ない時間帯に実行
lock_wait_timeout によるメタデータロック待ちの回避
学んだこと
- ALTER TABLE は開始時と完了時に短時間のメタデータロック(MDL)を取得する必要がある
- 長いトランザクションが MDL を掴んでいると、ALTER が待ち状態になり、さらに後続の全クエリが連鎖的にブロックされる(これが最大のリスク)
lock_wait_timeout をセッション単位で短く設定することで、MDL 取得に失敗した場合に即座にエラー終了させ、連鎖ブロックを防げる
- MDL が必要なのは ALTER の開始時と完了時の一瞬だけであり、INPLACE 処理中は DML をブロックしない
詳細
-- セッション単位で3秒に設定(デフォルトは1年)
SET SESSION lock_wait_timeout = 3;
ALTER TABLE your_table
ADD INDEX idx_col1_col2 (col1, col2),
ALGORITHM=INPLACE, LOCK=NONE;
-- MDL が3秒以内に取れなければエラーで即終了
-- → タイミングを変えてリトライすればよい
注意点:
SET SESSION で設定すること(GLOBAL だと他セッションに影響)
- 3〜5秒が実用的な値。短すぎると成功率が下がる
- ALTER 完了時にも MDL 取得があるが、一瞬なので問題になることは稀
EXPLAIN 出力の読み方 — 注視すべきポイント
学んだこと
- EXPLAIN の出力項目は多いが、最優先で見るべきは
type、rows、Extra の3つ
type: ALL(フルスキャン)が出たら最優先で対処。大量行テーブルでは致命的
- JOIN がある場合、
rows は掛け算で効いてくるため、各テーブルの rows を掛け合わせて全体のスキャン規模を把握する
Extra に Using temporary; Using filesort が両方出たら改善の優先度が高い
- 複合インデックスの活用度合いは
key_len で判断できる(短い場合、途中のカラムまでしか使われていない)
詳細
type カラム(アクセスタイプ)の評価順
| type |
意味 |
評価 |
const |
主キー/ユニークキーで1行特定 |
最速 |
eq_ref |
JOIN で主キー/ユニークキーで1行特定 |
良い |
ref |
非ユニークインデックスで複数行 |
良い |
range |
インデックスの範囲スキャン |
許容範囲 |
index |
インデックス全体をスキャン |
注意 |
ALL |
テーブルフルスキャン |
危険 |
| Extra の値 |
意味 |
対応 |
Using index |
カバリングインデックスで完結 |
最高 |
Using where |
インデックスで絞った後にさらに WHERE で絞っている |
普通 |
Using temporary |
一時テーブル作成 |
改善検討 |
Using filesort |
ソートにインデックスが使えていない |
改善検討 |
key_len による複合インデックスの活用度確認
-- インデックス: (user_id INT, status VARCHAR(20), created_at DATETIME)
-- key_len: 4 → user_id のみ使用
-- key_len: 86 → user_id + status まで使用
-- key_len: 91 → 全カラム使用
EXPLAIN チェックリスト
type が ALL になっていないか?
rows が想定より多すぎないか?(JOIN なら掛け算で考える)
Extra に Using temporary / Using filesort が出ていないか?
key が NULL になっていないか?
- 複合インデックスなら
key_len で何カラム目まで使われているか?
参考リンク
メタ情報
- ツール: Claude Code
- 関連技術: MySQL, InnoDB, Online DDL, EXPLAIN, インデックス設計, lock_wait_timeout