本番環境におけるALTER TABLEという名の恐怖
数億レコードにおよぶMySQLテーブルを管理するのは、決して容易なことではありません。「カラムの追加」や「データ型の変更」という依頼一つで、DBA(データベース管理者)は冷や汗をかくことになります。
駆け出しの頃の話ですが、MySQL 8.0で50GBのlogsテーブルを扱ったことがありました。当時はピーク時で同時アクセスユーザーが約500人ほど。私は自信満々に次のコマンドを入力しました:ALTER TABLE logs ADD COLUMN metadata JSON;。
わずか2秒後、監視アラートが真っ赤に。全クエリが「Waiting for table metadata lock」状態になり、サイトは読み込み中のまま(ぐるぐる)、上司が背後に立っていました。コマンドが完了するまでの30分間は、まさに息の詰まる思いでした。これは、巨大なデータベースを扱う際の無知が生んだ、手痛い教訓となりました。
なぜMySQLはテーブル構造の変更時にダウンタイムを引き起こすのか?
問題は、MySQLのOnline DDL(バージョン5.6以降)にはまだ多くの弱点があることです。「オンライン」と謳われつつも、実際にはシステムを「仮死状態」に追い込む可能性があります。
技術的には、構造を変更する際、MySQLは通常「テンポラリテーブルの作成、データのコピー、入れ替え」という手順を踏みます。この過程で、MySQLはメインテーブルに対して「排他ロック(Exclusive Lock)」をかけます。その結果、アプリケーションからの書き込み操作はすべてブロックされ、待機列に並びます。私の50GBのテーブルの場合、コピーに数十分かかるということは、その間アプリケーションが完全に「フリーズ」することを意味します。
一般的な解決策とそれに伴うリスク
最良の解決策を見つけるまで、私たちのチームは多くの手動の方法を試してきました:
1. MySQL Online DDL (ALGORITHM=INPLACE) を使用する
理論上は優れていますが、実際にはプロセスの最初と最後にメタデータロックが必要です。長時間実行されているクエリ(long-running query)がある場合、このロック取得待ちによって深刻なクエリの渋滞(キュー)が発生します。
2. 深夜2時にメンテナンスを行う
これは「技術不足を根性で補う」方法です。チーム全員で夜更かしし、アプリケーションとの接続を遮断してからコマンドを実行します。この方法は体力的にも厳しく、24時間365日の稼稼が求められるグローバルシステムでは現実的ではありません。
3. 手動でのシャドウテーブル(Shadow Table)
自分で新しいテーブルを作成し、10,000行ずつのブロック単位でデータをコピーした後、RENAME TABLEを使用する方法です。この方法は比較的安全ですが、コピー中に発生した新しいデータの同期が非常に困難です。
pt-online-schema-change: DBAにとっての強力な武器
数々の「苦い経験」を経て、私はPercona Toolkitに含まれるpt-online-schema-changeに出会いました。これは、ロックの心配をせずに巨大なテーブルを処理するための最良の選択肢とされています。
インテリジェントな仕組み
このツールは古いテーブルに直接干渉するのではなく、より賢いメカニズムを使用します:
- 新しい構造を持つ空の「シャドウ(shadow)」テーブルを作成します。
- トリガー(Triggers)(INSERT, UPDATE, DELETE)を設定し、元のテーブルからの変更を即座にシャドウテーブルに自動同期します。
- 元のデータを小さなブロック(例:1回につき1,000行)に分割して、徐々にコピーします。これにより、I/Oのボトルネックを回避し、CPU使用率の急増を防ぎます。
- コピー完了後、
RENAME TABLEを使用して、数ミリ秒で2つのテーブルを入れ替えます。 - 最後に、古いテーブルとトリガーをクリーンアップします。
インストールと実際の使用方法
Ubuntu環境では、インストールは非常に簡単です:
sudo apt-get update
sudo apt-get install percona-toolkit
例えば、ordersテーブルにstatusカラムを追加する必要があるとしましょう。直接コマンドを実行するのではなく、以下の手順を踏んでください:
# ステップ1: ドライラン(Dry Run)でエラーを確認
pt-online-schema-change --dry-run \
--alter "ADD COLUMN status tinyint(1) DEFAULT 1" \
h=localhost,D=my_database,t=orders,u=admin,p=password
# ステップ2: 実際に実行(Execute)
pt-online-schema-change --execute \
--max-load Threads_running=50 \
--alter "ADD COLUMN status tinyint(1) DEFAULT 1" \
h=localhost,D=my_database,t=orders,u=admin,p=password
重要なパラメータの注意点:
--dry-run: データに影響を与えずに、構文や制約をチェックできます。--max-load: 実行中のスレッド数が50を超えた場合に自動的に一時停止し、サーバーのオーバーロードを防ぎます。--alter: 実行したいテーブル構造の変更内容を指定します。
覚えておくべき実践的な経験則
数十GBのテーブルを扱ってきた実体験に基づき、3つの重要なポイントを挙げます:
1. トリガーに注意: このツールは同期にトリガーを利用するため、既存のトリガーがある場合はPerconaのドキュメントをよく確認してください。古いバージョンでは競合が発生し、データ損失の原因になる可能性があります。
2. 容量の問題: シャドウテーブルは元のテーブルと同等の容量を消費します。テーブルが50GBであれば、コピープロセスでディスクがいっぱいにならないよう、少なくとも60〜70GBの空き容量があることを確認してください。
3. 外部キー(Foreign Keys)の扱い: これは最も複雑な部分です。--alter-foreign-keys-method=autoパラメータを使用し、本番環境に適用する前に必ずステージング環境で十分にテストすることを強くお勧めします。
pt-online-schema-changeのおかげで、データベースのメンテナンスが格段に楽になりました。今では、ユーザーが遅延に気づくことさえなく、日中の真っ只中にインデックスを追加したりデータ型を変更したりすることができています。

