スキーマ変更をダウンタイムなしで行う方法

スキーマ変更を含むアプリケーションのリリースは、スキーマの変更中にリクエストを受け取るとデータの不整合が起きるため、基本的にダウンタイムが必要になる。スキーマの変更、つまり migration 作業に必要な時間が数秒程度であれば多くの場合で問題ない一方で、対象のテーブルが数万レコード程度のデータサイズになるとダウンタイムが無視できなくなる。
以下の記事にあるように データのコピーが必要になる migration は時間がかかってしまうようである。(時間だけでなくリソースも必要になる)
実際に数億レコード含むテーブルを rails で migration した場合は、数時間程度かかるようである。

なので、ダウンタイムを極力なくしてスキーマ変更を伴いたい場合には、変更したいカラムを直接変更するわけではなく、変更後に参照するテーブルを用意して、アプリケーションを古いテーブルで動かしながらデータを新しいテーブルにコピーしてくるという方針をとることが多い。
以下の例ではテーブルではなくカラムを用意しているが、カラムの追加はそこまで時間はかからない場合もありそうなので、テーブルごと用意するよりはお手軽にできそうである。 (カラムの追加自体はデータコピーが必要なようなので、データ数が数億レコードとかになると厳しいかもしれない)
100万レコードで数秒なので、データ量によっては深夜などに作業できるのであれば問題ないかもしれない。

上記の方法は、pt-online-schema-change というツールを利用して行う場合がある。pt-online-schema-change は、共有ロックを取らずに alter を可能にするツールである。(通常の alter は共有ロックをとるので、ロックしている時間が長くなるとサービスがダウンしてしまう)
このツールを使って、具体的に億単位のレコードを含むテーブルを migration した話
pt-online-schema-change の処理の流れ
- スキーマ変更対象 (以下、旧テーブル) と同じスキーマの新規テーブル (以下、新テーブル) を作成します。
- 新テーブルに対して、DDLを実行します。この時点で新テーブルは空なので、DDLは一瞬で完了します。
- 旧テーブルから新テーブルへデータの変更を反映するための、トリガーを作成します。このトリガーによって、旧テーブルへのデータの作成・削除・更新が新テーブルへ反映します
- 既存のデータをコピーするため、旧テーブルから読み出し、新テーブルへ書き込む処理が実行されます。
- 4.のデータコピーが完了すると、新テーブルと旧テーブルをRENAME TABLEによって入れ替えます。これによって、元のテーブル名で新しいスキーマのテーブルが利用できるようになります
- 後片付けとして、旧テーブルの削除とトリガーの削除が行われます。
次の点には注意する必要がある。
- 大量の読み込み/書き込みを行うので、CPUやIO等の負荷はそれなりにかかる
- データを一時的にコピーするため、ディスク容量も必要
リソースを消費するので、アクセスの多い日中にはなるべく実行しない方が良さそうである。ブログでもやられているが、データのコピーを深夜にだけ行うというようなこともできるようである。