マイグレーションとスキーマ

概要

Rails では マイグレーション (migration)スキーマ (schema) が連携して、データベース構造をコードで安全にバージョン管理できます。マイグレーションは「変更履歴」、スキーマは「現在の完成図」を表し、いずれも ActiveRecord によるオブジェクト指向データ操作の基盤です。本節では両者の役割と実践的な運用方法を詳述します。


1. マイグレーションとは

観点 内容
目的 データベースの「構造変更」を Ruby で宣言的に記述し、差分を自動適用する
本体 db/migrate/YYYYMMDDHHMMSS_add_email_to_users.rb のようなタイムスタンプ付き Ruby クラス
実行単位 バージョン番号(タイムスタンプ)が付けられ、順序どおりに適用/取り消し可能
永続管理 Git などの VCS にコミットして、チーム/CI で同じ履歴を再現

1.1 生成

bash
bin/rails generate migration AddEmailToUsers email:string:index

生成直後の雛形:

ruby
class AddEmailToUsers < ActiveRecord::Migration[7.1] def change add_column :users, :email, :string add_index :users, :email, unique: true end end
  • change可逆的。自動で up / down が推測されます。

  • 破壊的操作(execute "DROP TABLE ..." など)は up / down個別に 定義。

1.2 適用・管理コマンド

コマンド 説明
bin/rails db:migrate まだ適用されていないマイグレーションを順に実行
bin/rails db:rollback STEP=1 直近 n 件を取り消し (STEP 省略で 1)
bin/rails db:migrate:status 適用済み/未適用を一覧表示
bin/rails db:version 現在のスキーマバージョンを表示

ActiveRecord は schema_migrations テーブルで 適用済みバージョン を追跡します。


2. スキーマ定義ファイル

2.1 db/schema.rb

  • マイグレーションを 全部適用した結果 を Ruby DSL で表現。

  • デフォルトで bin/rails db:migrate 後に自動更新。

  • 非常に高速にロードでき、テスト用データベース作成 (bin/rails db:test:prepare) でも使用。

2.2 db/structure.sql

  • schema.rb が表現できない DB 固有機能(CHECK 制約、トリガーなど) を完全保持。

  • config.active_record.schema_format = :sql で出力形式を切替。

選択指針

  • ポータビリティ重視schema.rb

  • DB 固有機能を多用structure.sql


3. マイグレーションのライフサイクル

  1. 作成rails generate migration

  2. コードレビュー:命名規則・インデックス・NOT NULL 制約確認

  3. 適用:CI/CD で rails db:migrate を自動実行

  4. 検証:アプリ起動時に PendingMigrationError がないことを確認

  5. 運用:本番でロールバックが難しい破壊的変更は 2 段階 で実施

    • ① 新カラム追加 → 両対応コード

    • ② 旧カラム削除


4. データマイグレーションの扱い

  • 構造変更とデータ更新を分離するとレビュー・ロールバックが容易。

    • 構造: 20250621120000_add_status_to_orders.rb

    • データ: 20250621121000_migrate_order_status.rbupdate_all 等)

  • 大量データは バッチ(Rake タスク/ActiveJob)や オンラインマイグレーションツール(gh-ost, pt-online-schema-change 等)を選択。


5. ベストプラクティス

  1. わかりやすい名前

    • AddDeletedAtToUsers など変更内容+対象テーブルを明示

  2. インデックスは同時追加

    • 後から追加するとロールバック時に整合性問題が起きやすい

  3. NOT NULL 制約は 2 段階

    1. カラム追加 (null: true) → 既存行更新 → 2) 制約付与 (change_column_null)

  4. 長時間ロック回避

    • add_column_with_default を避け、default: をマイグレーション後に change_column_default で設定

  5. db/seed.rb と混同しない

    • seeds は 初期データ投入用。構造変更はマイグレーションで行う

  6. idempotent なコードを書く

    • index_exists?, column_exists? で多重適用エラーを防止

  7. 本番直前のロールバック不可対策

    • safety_assured {}(gem strong_migrations)や メンテナンスウィンドウを設定


6. よく使う DSL サンプル

ruby
# 外部キーと ON DELETE CASCADE class AddAuthorRefToPosts < ActiveRecord::Migration[7.1] def change add_reference :posts, :author, null: false, foreign_key: { to_table: :users, on_delete: :cascade } end end
ruby
# 複合インデックス add_index :orders, %i[user_id created_at], name: "idx_orders_user_created_at"
ruby
# ENUM を列挙値で表現(PostgreSQL) def up execute <<~SQL CREATE TYPE payment_status AS ENUM ('pending', 'paid', 'failed'); SQL add_column :payments, :status, :payment_status, null: false, default: 'pending' end def down remove_column :payments, :status execute "DROP TYPE payment_status" end

7. 参考コマンド一覧

bash
# マイグレーション生成(カラム指定) bin/rails g migration AddPublishedAtToArticles published_at:datetime:index # スキーマファイルのみ再生成(マイグレーションは実行しない) bin/rails db:schema:dump # 本番 DB に接続確認してマイグレーション RAILS_ENV=production bin/rails db:migrate # 未適用があるとテスト実行前にエラー bin/rails test

まとめ

  • マイグレーションは時系列で蓄積される「差分スクリプト」、スキーマファイルはその結果を示す「最新設計図」。

  • ActiveRecord が schema_migrations テーブルと DSL を用いて、DB の状態を Ruby コードで安全に同期する。

  • 破壊的変更・大量データ移行はフェーズ分割し、ロック時間とロールバックリスクを最小化する。

  • schema.rbstructure.sql の選択は用途で切り替え、CI/CD で自動検証を徹底する。

以上が Rails モデルレイヤにおける マイグレーションとスキーマ の詳細です。

ChatGPT4o 生成日:2025/06/21