スキーママイグレーション
Schema Migration
データベースの構造変更を版管理されたコードとして扱い、どの環境でも再現可能にする仕組み。
概要
スキーママイグレーションは、データベースの構造(スキーマ)の変更 — テーブルの追加、カラムの変更、インデックスの作成など — を、番号や日時で順序付けられた「マイグレーションファイル」として版管理し、どの環境でも同じ手順で再現できるようにする仕組みです。アプリケーションのコードを Git で管理するのと同じ発想を、データベースの構造に適用したものと言えます。
「migrate」というコマンドを実行すると、そのデータベースにまだ適用されていない変更だけが古い順に適用され、開発者のローカル環境・テスト環境・本番環境のスキーマが同じ履歴をたどって同じ状態に揃います。Rails の Active Record Migrations、Django の migrations、Flyway、Liquibase など、ほとんどのWebフレームワークや ORM が標準機能または定番ツールとして備えており、現代のデータベース運用の前提となっています。
なぜ生まれたか
マイグレーション以前、スキーマの変更は「DBAが本番データベースに手作業で ALTER 文を流す」作業でした。この方法には根本的な問題があります。変更の記録が人の記憶やメモにしか残らず、開発環境と本番環境のスキーマが少しずつずれていく。新しいメンバーの環境構築では「最新のスキーマを誰かのDBからダンプしてくる」しかなく、それが正しい状態なのか誰にも分からない。そしてアプリケーションのコードは SQL の版管理とは無関係にデプロイされるため、「新しいコードが、まだ存在しないカラムを参照して落ちる」といった事故が頻発しました。
スキーママイグレーションは、この「コードは版管理されるのに、それが依存するDB構造は管理されない」という非対称を解消するために生まれました。スキーマ変更をコードと同じリポジトリに置けば、コードレビューの対象になり、コードと同じブランチ・同じデプロイの流れに乗り、「このコミットの時点でスキーマはどうだったか」が常に再現できるようになります。
詳細
マイグレーションファイルと up / down
マイグレーションは「1つの変更 = 1ファイル」を基本とし、ファイル名の連番やタイムスタンプで適用順序を決めます。各ファイルには、変更を適用する up(例: カラムを追加する)と、それを取り消す down(例: そのカラムを削除する)の2方向を記述します。ツールはデータベース内に専用の管理テーブルを持ち、「どのマイグレーションまで適用済みか」を記録します。migrate コマンドは未適用分だけを順に実行し、rollback コマンドは down を使って直前の状態に戻します。
ただし down には限界があります。カラム削除の down は「カラムを作り直す」ことはできても、消えたデータまでは戻せません。実務では down は開発中の試行錯誤のためのものと割り切り、本番で問題が起きたときは「戻す」のではなく「打ち消す新しいマイグレーションを前に進める(roll forward)」のが安全とされています。
難しいのは「動いているシステムを止めずに変える」こと
マイグレーションの本当の難所は構文ではなく、稼働中のシステムとの整合です。デプロイの瞬間、新旧バージョンのアプリケーションは一時的に共存します。ブルーグリーンデプロイやローリング更新では、この共存は仕組み上避けられません。ここで「カラム名を rename する」マイグレーションを一発で流すと、旧コードは消えた旧カラムを参照して即座にエラーになります。
そこで使われるのが expand-contract パターン(parallel change とも呼ばれます)です。破壊的な変更を一度に行わず、「広げる(expand)→ 移行する(migrate)→ 縮める(contract)」の3段階に分けます。
まず新カラムを「追加だけ」する(旧コードは新カラムを知らないままでも壊れません)。次にアプリを新旧両方のカラムに書き込むよう更新し、既存データをバックグラウンドでコピーし、読み取りを新カラムへ切り替えます。全インスタンスが新カラムだけを使うようになったことを確認して、最後に旧カラムを削除する。各段階の間には日をまたいでもよく、どの時点でも新旧コードが共存できる — つまり各マイグレーションが常に後方互換であることが、このパターンの核心です。
CI/CD への組み込みと運用上の注意
マイグレーションはコードと一緒にリポジトリに入るため、CI/CD パイプラインに自然に組み込めます。CIではまっさらなDBに全マイグレーションを適用してテストを走らせることでスキーマの再現性を検証し、デプロイ時には「アプリの新バージョン起動前に migrate を実行する」のが典型的な流れです。expand-contract を守っていれば、マイグレーション適用と新コード配布の順序に神経質にならずに済みます。複数人開発では、別ブランチで作られたマイグレーション同士が合流時に順序衝突することがあるため、タイムスタンプ方式の採番や、マージ後にCIで全適用を検証する運用が安全網になります。
もうひとつの実務的な注意は、大きなテーブルへの変更です。数千万行のテーブルに対する ALTER やインデックス作成は長時間テーブルをロックし、それ自体が障害になり得ます。PostgreSQL の CREATE INDEX CONCURRENTLY のようなロックを避ける構文や、gh-ost・pt-online-schema-change のようなオンラインスキーマ変更ツールを使い、「マイグレーションは瞬時に終わる小さな変更の積み重ねにする」ことが大規模運用の定石です。スキーマという最も壊してはいけない状態を、レビュー可能で再現可能な小さな一歩の連続として前進させる — それがスキーママイグレーションという実践の本質です。
