Flyway
Flywayデータベースマイグレーションツール完全ガイド
目次
- 導入
- Flywayの基本概念
- アーキテクチャ概要
- インストールと初期設定
- マイグレーション戦略
- 実践的な設定例
- マイグレーションバージョニング
- 本番環境への適用
- トラブルシューティング
- ベストプラクティス
1. 導入
1.1 Flywayとは
Flyway(フライウェイ)は、Axoniq社により開発・保守されているオープンソースのデータベースマイグレーションツールです。アプリケーションのバージョン管理と同様に、データベーススキーマのバージョン管理を自動化し、チーム開発における安全かつ予測可能なデータベース進化を実現します。
Flywayの核となる哲学は「Version Control for your Database」です。この考え方により、以下のような課題を解決します:
- スキーマのドリフト:異なるデータベース環境間のスキーマ定義の不一致
- マイグレーション漏れ:本番環境への適用忘れや不完全な適用
- ロールバックの困難さ:データベース変更の巻き戻し処理
- 開発チーム間の同期不足:マイグレーション実行順序の不確定性
1.2 主要な特徴
- シンプルで直感的: SQLファイルを配置するだけでマイグレーション実行
- 言語非依存: JVM、CLI、Docker、Gradle、Maven、.NET等で利用可能
- 複数データベース対応: PostgreSQL、MySQL、Oracle、SQL Server等をサポート
- 自動バージョン管理: マイグレーション履歴を自動トラッキング
- トランザクション安全性: ほとんどのマイグレーション実行をトランザクション内で実行
- CLI・API・プラグイン: 様々な統合方法をサポート
1.3 適用シーン
Flywayは以下のような環境で特に有効です:
- マイクロサービスアーキテクチャでの各サービスのスキーマ管理
- CI/CDパイプラインへの統合
- 複数の開発環境(開発機、ステージング、本番)の管理
- Dockerコンテナ化されたアプリケーション
- DevOps文化を採用する組織
2. Flywayの基本概念
2.1 マイグレーション(Migration)
マイグレーションとは、データベーススキーマの段階的な変更を記述したファイルセットです。Flywayでは以下の2種類があります:
2.1.1 Versioned Migration(バージョン管理マイグレーション)
順序付きのスキーマ変更を表現します。命名規則は以下の通りです:
V<version>__<description>.sql
例:
V1__Create_users_table.sql
V2__Add_email_to_users.sql
V3__Create_products_table.sql
V1.1__Add_index_on_users_id.sql
バージョン番号の仕様:
- 数値のみで構成
- ドットで区切られた複数の数値をサポート(1.0、1.1、2.0.1等)
- アンダースコアで説明文と分割
- 説明文中のアンダースコアが単語の区切り
2.1.2 Undo Migration(アンドゥ・マイグレーション)
バージョン管理マイグレーションを取り消すマイグレーション。命名規則:
U<version>__<description>.sql
例:
U1__Create_users_table.sql
U2__Add_email_to_users.sql
重要: Undo機能を使用するには、エディション選択およびアンドゥ機能の有効化が必要です。本番環境ではロールバックより新しいマイグレーションでの修正推奨。
2.1.3 Repeatable Migration(繰り返し可能マイグレーション)
バージョン不要で、変更検出時に繰り返し実行されるマイグレーション。命名規則:
R__<description>.sql
例:
R__Create_roles.sql
R__Populate_lookup_tables.sql
R__Refresh_materialized_views.sql
用途:
- ビュー定義
- 関数定義
- 初期データの挿入
- 権限設定
- マテリアライズドビューのリフレッシュ
実行タイミング:
- ファイルが初回作成時
- チェックサムが変更された時(コンテンツ変更検出)
2.2 Flyway Schema History テーブル
Flywayは全マイグレーション実行履歴をflyway_schema_historyテーブルに保存します。
テーブル構造:
CREATE TABLE flyway_schema_history (
installed_rank INT NOT NULL,
version VARCHAR(50),
description VARCHAR(255) NOT NULL,
type VARCHAR(20) NOT NULL,
script VARCHAR(1000) NOT NULL,
checksum INT,
installed_by VARCHAR(100) NOT NULL,
installed_on TIMESTAMP NOT NULL,
execution_time INT NOT NULL,
success BOOLEAN NOT NULL
);
主要カラム説明:
installed_rank: インストール順序(1から開始、単調増加)version: マイグレーションバージョン(V マイグレーションのみ)description: マイグレーション説明type: マイグレーション型(BASELINE, SQL, JDBC等)script: スクリプトファイル名checksum: ファイル内容のチェックサム(変更検出用)installed_by: 実行ユーザー名installed_on: 実行日時execution_time: 実行時間(ミリ秒)success: 実行成功/失敗
2.3 ステートマシン
Flywayのマイグレーション状態遷移:
初期状態
↓
マイグレーション検出 → V1が見つかった
↓
V1実行
↓
成功 → flyway_schema_historyに記録
↓
マイグレーション完了状態
↓
(次回実行時)
↓
V1の検証(チェックサム確認)
↓
チェックサムが一致 → スキップ
チェックサムが不一致 → エラー(検証失敗)
新規V2が見つかった → V2実行
3. アーキテクチャ概要
3.1 全体構成
┌─────────────────────────────────────────┐
│ Application Layer │
│ (Java App / CLI / Gradle / Maven) │
└──────────────────┬──────────────────────┘
│
↓
┌─────────────────────────────────────────┐
│ Flyway Core Engine │
│ ┌─────────────────────────────────────┐ │
│ │ 1. Configuration │ │
│ ├─────────────────────────────────────┤ │
│ │ 2. Migration Scanner │ │
│ ├─────────────────────────────────────┤ │
│ │ 3. Version Resolver │ │
│ ├─────────────────────────────────────┤ │
│ │ 4. State Checker │ │
│ ├─────────────────────────────────────┤ │
│ │ 5. Executor │ │
│ ├─────────────────────────────────────┤ │
│ │ 6. Validator │ │
│ └─────────────────────────────────────┘ │
└──────────────────┬──────────────────────┘
│
↓
┌─────────────────────────────────────────┐
│ Database Connection Manager │
│ (JDBC / Connection Pool) │
└──────────────────┬──────────────────────┘
│
↓
┌─────────────────────────────────────────┐
│ Target Database │
│ (PostgreSQL / MySQL / Oracle / etc) │
└─────────────────────────────────────────┘
3.2 コンポーネント詳細
3.2.1 Configuration(設定)
Flywayの動作を制御する設定層。設定項目例:
locations: マイグレーションファイルの配置場所baselineVersion: 初期バージョン番号validateOnMigrate: マイグレーション実行前の検証有効化cleanDisabled: データベースクリーニング禁止フラグsqlMigrationPrefix: VマイグレーションのプリフィックスrepeatableSqlMigrationPrefix: Rマイグレーションのプリフィックス
3.2.2 Migration Scanner(スキャナー)
マイグレーションファイルを発見するコンポーネント。
処理流程:
locationsで指定されたディレクトリをスキャン- 命名規則に合致するファイルを収集
- ファイルシステムおよびクラスパスから読み込み
- 発見されたマイグレーションをリスト化
スキャン対象:
- ファイルシステム上のSQL/JAVAファイル
- クラスパス上のリソース(JAR内含む)
- ネットワークパス(設定可能)
3.2.3 Version Resolver(バージョン解決)
発見されたマイグレーションをバージョン順にソート・解決。
処理:
- バージョン番号を数値として解析
- Versionedマイグレーションを版順にソート
- バージョン重複チェック(重複あればエラー)
- Repeatableマイグレーションを整理
3.2.4 State Checker(状態検査)
データベース現在状態の確認。
確認内容:
- スキーマ履歴テーブルの存在確認
- 現在のマイグレーション状態確認
- 実行済みマイグレーションの一覧取得
- 検証失敗有無の確認
3.2.5 Executor(実行エンジン)
実際のマイグレーション実行。
実行プロセス:
- トランザクション開始
- マイグレーションスクリプト実行
- 実行ログをスキーマ履歴テーブルに記録
- トランザクションコミット
トランザクション管理:
- ほとんどのマイグレーション実行をトランザクション内で実施
- 実行失敗時は自動ロールバック
- DDL(CREATE/ALTER等)がトランザクション非対応の場合は個別対応
3.2.6 Validator(検証エンジン)
マイグレーション整合性の検証。
検証項目:
- チェックサムの一致確認
- マイグレーションスクリプト構文チェック
- 実行順序の妥当性確認
- 実行済みマイグレーション未変更の確認
3.3 処理フロー
┌─────────────────────────────────┐
│ Flyway実行開始 │
│ (migrate/validate/info等) │
└────────────┬────────────────────┘
│
↓
┌─────────────────────────────────┐
│ Database接続確立 │
└────────────┬────────────────────┘
│
↓
┌─────────────────────────────────┐
│ スキーマ履歴テーブル確認 │
│ 未作成 → 作成 │
└────────────┬────────────────────┘
│
↓
┌─────────────────────────────────┐
│ マイグレーション検出 │
│ locations配下をスキャン │
└────────────┬────────────────────┘
│
↓
┌─────────────────────────────────┐
│ 検出マイグレーション │
│ バージョン順にソート │
└────────────┬────────────────────┘
│
↓
┌─────────────────────────────────┐
│ 未実行マイグレーション特定 │
│ 履歴テーブルと比較 │
└────────────┬────────────────────┘
│
↓
┌─────────────────────────────────┐
│ ┌────────────────────────────┐ │
│ │ マイグレーション検証 │ │
│ │ (validateOnMigrate有効時) │ │
│ └────────────┬───────────────┘ │
│ │ │
│ ┌─────▼─────┐ │
│ │ 検証成功 │ │
│ └─────┬─────┘ │
└──────────────┬──────────────────┘
│
↓
┌─────────────────────────────────┐
│ ┌────────────────────────────┐ │
│ │ マイグレーション実行 │ │
│ │ (未実行 → 実行) │ │
│ └────────────┬───────────────┘ │
│ │ │
│ ┌─────▼──────┐ │
│ │ 実行成功 │ │
│ └─────┬──────┘ │
│ │ │
│ ┌─────▼──────────────┐ │
│ │ 履歴テーブルに記録 │ │
│ └─────┬──────────────┘ │
└──────────────┬──────────────────┘
│
↓
┌─────────────────────────────────┐
│ 全マイグレーション完了 │
│ (必要に応じて繰り返し実行) │
└────────────┬────────────────────┘
│
↓
┌─────────────────────────────────┐
│ 実行結果出力 │
└─────────────────────────────────┘
4. インストールと初期設定
4.1 インストール方法
4.1.1 Javaアプリケーションへの組み込み
Maven統合
pom.xmlに以下を追加:
<dependencies>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>9.22.3</version>
</dependency>
<!-- データベースドライバ -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
</dependency>
</dependencies>
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>9.22.3</version>
<configuration>
<url>jdbc:postgresql://localhost:5432/myapp</url>
<user>postgres</user>
<password>password</password>
</configuration>
</plugin>
</plugins>
Gradle統合
build.gradleに以下を追加:
plugins {
id 'org.flywaydb.flyway' version '9.22.3'
}
dependencies {
implementation 'org.flywaydb:flyway-core:9.22.3'
runtimeOnly 'org.postgresql:postgresql:42.6.0'
}
flyway {
url = 'jdbc:postgresql://localhost:5432/myapp'
user = 'postgres'
password = 'password'
locations = ['filesystem:src/main/resources/db/migration']
}
4.1.2 コマンドラインツール(CLI)
ダウンロード・インストール
# macOS/Linux
wget https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/9.22.3/flyway-commandline-9.22.3-linux-x64.tar.gz
tar xzf flyway-commandline-9.22.3-linux-x64.tar.gz
cd flyway-9.22.3
./flyway -version
# またはHomebrew(macOS)
brew install flyway
flyway -version
基本コマンド
# マイグレーション実行
flyway -url=jdbc:postgresql://localhost:5432/myapp \
-user=postgres \
-password=password \
-locations=filesystem:db/migration \
migrate
# 現在状態確認
flyway -url=jdbc:postgresql://localhost:5432/myapp \
-user=postgres \
-password=password \
info
# 検証実行
flyway -url=jdbc:postgresql://localhost:5432/myapp \
-user=postgres \
-password=password \
validate
4.1.3 Docker
Flywayの公式Dockerイメージ利用:
FROM flyway/flyway:9.22.3
COPY db/migration /flyway/sql
CMD ["flyway", "migrate"]
実行方法:
docker run --rm \
-v $(pwd)/db/migration:/flyway/sql \
-e FLYWAY_URL=jdbc:postgresql://postgres:5432/myapp \
-e FLYWAY_USER=postgres \
-e FLYWAY_PASSWORD=password \
flyway/flyway:9.22.3 migrate
4.2 初期設定
4.2.1 マイグレーションディレクトリ構造
推奨ディレクトリ構造:
project-root/
├── src/
│ ├── main/
│ │ ├── java/
│ │ └── resources/
│ │ └── db/
│ │ └── migration/
│ │ ├── V1__Initial_schema.sql
│ │ ├── V2__Add_users_table.sql
│ │ ├── V3__Create_products_table.sql
│ │ ├── V4__Add_indexes.sql
│ │ └── R__Refresh_views.sql
│ └── test/
│ └── resources/
│ └── db/
│ └── migration/
│ └── test/
│ ├── V1__Initial_schema.sql
│ └── V2__Insert_test_data.sql
└── flyway.conf
4.2.2 設定ファイル(flyway.conf)
プロジェクト直下にflyway.confを作成:
# Database接続設定
flyway.url=jdbc:postgresql://localhost:5432/myapp_dev
flyway.user=postgres
flyway.password=your_password
flyway.driver=org.postgresql.Driver
# マイグレーション設定
flyway.locations=filesystem:src/main/resources/db/migration
flyway.baselineOnMigrate=false
flyway.baselineVersion=0
# マイグレーション実行設定
flyway.validateOnMigrate=true
flyway.cleanDisabled=true
flyway.outOfOrder=false
# スキーマ・テーブル設定
flyway.schemas=public
flyway.table=flyway_schema_history
# ロギング設定
flyway.placeholderReplacement=true
flyway.placeholders.app_version=1.0.0
flyway.placeholders.migration_user=db_admin
4.2.3 環境別設定
異なる環境用設定ファイル:
flyway-dev.conf
flyway.url=jdbc:postgresql://localhost:5432/myapp_dev
flyway.user=dev_user
flyway.password=dev_password
flyway.validateOnMigrate=true
flyway.cleanDisabled=false
flyway-prod.conf
flyway.url=jdbc:postgresql://prod-db.company.com:5432/myapp
flyway.user=prod_user
flyway.password=${DATABASE_PASSWORD}
flyway.validateOnMigrate=true
flyway.cleanDisabled=true
flyway.outOfOrder=false
実行時に環境別設定を指定:
flyway -configFiles=flyway-prod.conf migrate
5. マイグレーション戦略
5.1 バージョニング戦略
5.1.1 セマンティックバージョニング型
メジャー.マイナー.パッチ形式:
V1__Initial_schema.sql
V1.1__Add_column_to_users.sql
V1.2__Create_index.sql
V2__Major_schema_redesign.sql
V2.1__Fix_column_type.sql
用途:
- 大きな機能追加 → メジャー版アップ
- スキーマ拡張 → マイナー版アップ
- バグ修正・最適化 → パッチ版アップ
5.1.2 タイムスタンプ型
YYYYMMDDHHmmss形式:
V20240101120000__Create_users_table.sql
V20240105143000__Add_products_table.sql
V20240110090000__Create_indexes.sql
利点:
- 実行時刻の追跡が容易
- 並列開発で競合しにくい
- Git履歴との対応づけが明確
欠点:
- バージョン番号が長い
- 意図不明確(何の変更かが見づらい)
5.1.3 チケット番号型
チケット・イシュー番号を含める:
V1__PROJ-123_Create_users_table.sql
V2__PROJ-456_Add_email_validation.sql
V3__PROJ-789_Create_audit_log.sql
利点:
- 変更理由の追跡が容易
- プロジェクト管理ツールとの連携
- チェンジログ自動生成が可能
5.2 スキーマ進化パターン
5.2.1 テーブル追加パターン
-- V1__Create_initial_schema.sql
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- V2__Create_posts_table.sql
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
title VARCHAR(500) NOT NULL,
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- V3__Create_comments_table.sql
CREATE TABLE comments (
id BIGSERIAL PRIMARY KEY,
post_id BIGINT NOT NULL REFERENCES posts(id),
user_id BIGINT NOT NULL REFERENCES users(id),
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
5.2.2 カラム追加パターン
-- V4__Add_profile_to_users.sql
ALTER TABLE users ADD COLUMN profile_bio TEXT;
ALTER TABLE users ADD COLUMN avatar_url VARCHAR(500);
ALTER TABLE users ADD COLUMN updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
-- V5__Add_indexes_for_performance.sql
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_comments_post_id ON comments(post_id);
5.2.3 カラム削除パターン(推奨方法)
段階的削除パターン:
-- V6__Deprecate_unused_column.sql
-- Step 1: アプリケーション側でカラムの使用を停止
-- (マイグレーション側では何もしない、またはコメント)
-- ALTER TABLE users DROP COLUMN deprecated_field;
-- V7__Remove_deprecated_column.sql
-- Step 2: 十分な期間(1-2スプリント)経過後に削除
ALTER TABLE users DROP COLUMN deprecated_field;
注意: 本番環境でのカラム削除は慎重に
5.2.4 制約変更パターン
-- V8__Add_unique_constraint.sql
ALTER TABLE users ADD CONSTRAINT uq_users_email UNIQUE(email);
-- V9__Add_not_null_constraint.sql
-- NOT NULLの追加は段階的に
UPDATE users SET phone_number = '' WHERE phone_number IS NULL;
ALTER TABLE users ADD CONSTRAINT nn_phone_number CHECK(phone_number IS NOT NULL);
5.2.5 データ型変更パターン
-- V10__Change_column_type.sql
-- PostgreSQL例
ALTER TABLE products
ALTER COLUMN price TYPE NUMERIC(10,2) USING price::NUMERIC(10,2);
-- MySQL例
ALTER TABLE products
MODIFY COLUMN price DECIMAL(10,2);
5.3 並行開発での マイグレーション管理
5.3.1 マージコンフリクト回避
チーム開発でのシナリオ:
開発者A: 機能ブランチA作成
└─ V1__Initial.sql存在
開発者B: 機能ブランチB作成
└─ V1__Initial.sql存在
開発者A: V2__Feature_A.sqlを作成・コミット
開発者B: V2__Feature_B.sqlを作成・コミット
結果: バージョン競合 (V2が2つ存在)
解決策: タイムスタンプ型またはチケット型を使用
開発者A: V20240110120000__Feature_A.sql
開発者B: V20240110140000__Feature_B.sql
またはチケット型:
開発者A: V2__PROJ-123_Feature_A.sql
開発者B: V3__PROJ-456_Feature_B.sql
5.3.2 リベース時の対応
# ローカルでリベース前
flyway clean # テスト環境でクリーニング
git rebase main
# リベース後、新マイグレーションの整序確認
flyway validate
# マイグレーション再実行
flyway migrate
5.4 ロールバック戦略
5.4.1 推奨: 新マイグレーションでの修正
-- V1__Add_column.sql (失敗した変更)
ALTER TABLE users ADD COLUMN age INT NOT NULL DEFAULT 0;
-- V2__Fix_previous_error.sql (修正)
ALTER TABLE users DROP COLUMN age;
ALTER TABLE users ADD COLUMN age INT DEFAULT NULL;
5.4.2 バージョンマッピング方式
-- V1__Initial_schema.sql
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE
);
-- V2__Add_age_column.sql (失敗)
ALTER TABLE users ADD COLUMN age INT NOT NULL;
-- V3__Remove_age_column.sql (修正)
ALTER TABLE users DROP COLUMN age;
6. 実践的な設定例
6.1 Spring Bootアプリケーション統合
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>flyway-demo</artifactId>
<version>1.0.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
</parent>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Flyway -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<!-- Database -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<configuration>
<url>${flyway.url}</url>
<user>${flyway.user}</user>
<password>${flyway.password}</password>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/myapp
username: postgres
password: password
driver-class-name: org.postgresql.Driver
flyway:
enabled: true
locations: classpath:db/migration
baselineOnMigrate: false
validateOnMigrate: true
cleanDisabled: true
outOfOrder: false
encoding: UTF-8
sqlMigrationPrefix: V
repeatableSqlMigrationPrefix: R
logging:
level:
org.flywaydb: INFO
アプリケーション起動時の動作
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// 起動時にFlywayが自動実行される
// 1. Flyway初期化
// 2. マイグレーション検出・検証
// 3. 未実行マイグレーションを順序に実行
// 4. アプリケーション起動
6.2 マルチスキーマ対応
複数のスキーマを管理する場合の設定:
application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/myapp
username: postgres
password: password
flyway:
schemas: schema1,schema2,schema3
baselineOnMigrate: true
validateOnMigrate: true
# スキーマ1用マイグレーション
locations:
- classpath:db/migration/schema1
- classpath:db/migration/schema2
マイグレーションファイル構成
src/main/resources/db/migration/
├── schema1/
│ ├── V1__Create_schema1_tables.sql
│ └── V2__Add_schema1_indexes.sql
└── schema2/
├── V1__Create_schema2_tables.sql
└── V2__Add_schema2_indexes.sql
SQL例
-- V1__Create_schema1_tables.sql
CREATE SCHEMA IF NOT EXISTS schema1;
CREATE TABLE schema1.users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(255) NOT NULL
);
-- V1__Create_schema2_tables.sql
CREATE SCHEMA IF NOT EXISTS schema2;
CREATE TABLE schema2.products (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
6.3 プレースホルダー(変数置換)
動的な値をマイグレーション内で使用:
flyway.conf
flyway.placeholderReplacement=true
flyway.placeholders.app_user=app_user
flyway.placeholders.admin_user=admin_user
flyway.placeholders.schema=public
マイグレーションSQL
-- V1__Create_users_and_roles.sql
CREATE TABLE ${schema}.users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(255) NOT NULL
);
-- 権限設定
GRANT SELECT, INSERT, UPDATE ON ${schema}.users TO ${app_user};
GRANT ALL PRIVILEGES ON ${schema}.users TO ${admin_user};
実行時の変数値指定:
flyway -placeholders.app_user=myapp \
-placeholders.admin_user=admin \
-placeholders.schema=public \
migrate
6.4 環境別マイグレーション
異なる環境(開発・ステージング・本番)で異なるマイグレーションを実行:
ディレクトリ構成
src/main/resources/db/migration/
├── V1__Initial_schema.sql # 全環境
├── V2__Create_base_tables.sql # 全環境
└── dev/
├── V100__Create_test_data.sql
└── V101__Create_dev_views.sql
Spring Profileの使用
# application-dev.yml
spring:
flyway:
locations:
- classpath:db/migration
- classpath:db/migration/dev
# application-prod.yml
spring:
flyway:
locations:
- classpath:db/migration
# devディレクトリは含まない
実行方法:
# 開発環境
java -jar app.jar --spring.profiles.active=dev
# 本番環境
java -jar app.jar --spring.profiles.active=prod
7. マイグレーションバージョニング
7.1 バージョン番号の形式
Flywayが認識するバージョン番号:
1 (単一の数値)
1.0 (ドット区切り)
1.0.1 (複数レベル)
2023.01.15 (年月日)
1.2.3.4.5.6 (複数レベルのドット)
制限事項:
- 最初の文字は数値でなければならない
- ドット、アンダースコアのみ使用可能
- 数値のみで構成(プリフィックス・サフィックスなし)
7.2 チェックサム機構
マイグレーションファイル変更検出メカニズム:
7.2.1 チェックサム計算方法
// Flywayの内部処理
MD5 checksum = MD5.calculate(sqlMigrationContent);
各マイグレーション実行時にファイルのMD5ハッシュを計算:
V1__Initial.sql の内容がファイルから計算
→ MD5("CREATE TABLE users...")
→ "a1b2c3d4e5f6..."
→ flyway_schema_historyに記録
次回実行時に再度計算
→ チェックサムが一致 → スキップ
→ チェックサムが不一致 → エラー(破損警告)
7.2.2 チェックサムエラー対応
-- エラーが発生した場合
-- Error: Checksum mismatch of migration V1__Initial.sql
-- 修正方法1: マイグレーション新規作成(推奨)
-- V2__Fix_previous_migration.sql で修正内容を記述
-- 修正方法2: チェックサム再計算(非推奨)
-- データベースのflyway_schema_historyのchecksumをNULLに設定
UPDATE flyway_schema_history
SET checksum = NULL
WHERE version = '1';
7.3 バージョン競合解決
複数の開発者が同一バージョンのマイグレーションを作成した場合:
シナリオ1: 同じバージョン番号
開発者A: V2__Feature_A.sql
開発者B: V2__Feature_B.sql
エラー:
org.flywaydb.core.api.FlywayException:
Detected version duplicates: V2
解決方法: リベースして番号を付け直す
開発者A: V2__Feature_A.sql (チェックアウト)
開発者B: V3__Feature_B.sql (リネーム)
シナリオ2: マージ後の不整合
# Git上の順序
V1__Initial.sql
V2__Feature_B.sql (新規)
V3__Feature_A.sql (リネーム)
# しかし実行順序は V1 → V2 → V3 → ...
# チェックサムが全て保存されるため競合なし
8. 本番環境への適用
8.1 本番環境での安全対策
8.1.1 設定のベストプラクティス
# 本番環境用 flyway-prod.conf
# Database接続 - 環境変数から読み込み
flyway.url=${DATABASE_URL}
flyway.user=${DATABASE_USER}
flyway.password=${DATABASE_PASSWORD}
# セキュリティ設定
flyway.cleanDisabled=true # テーブルクリーニング禁止
flyway.outOfOrder=false # 順序外実行禁止
flyway.validateOnMigrate=true # 事前検証を実施
# トランザクション設定
flyway.validateOnMigrate=true
flyway.placeholderReplacement=true
# スキーマ履歴
flyway.table=flyway_schema_history
flyway.schemas=public
# ロギング
flyway.loggers=org.flywaydb
8.1.2 実行前チェックリスト
本番環境へのマイグレーション実行前:
- ステージング環境で完全テスト実施
- マイグレーションのバックアップ確認
- ロールバック手順の検証
- マイグレーション実行時間の推定
- ダウンタイムの予測と通知
- 実行ユーザーの権限確認
- 本番DBへの最新バックアップ作成
- 監視システムの確認
8.2 CI/CDパイプライン統合
8.2.1 GitHubActions統合例
# .github/workflows/database-migration.yml
name: Database Migration
on:
push:
branches: [main]
paths:
- 'src/main/resources/db/migration/**'
pull_request:
paths:
- 'src/main/resources/db/migration/**'
jobs:
validate:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: testdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v3
- name: Set up Java
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'adopt'
- name: Validate migrations
run: |
mvn flyway:validate \
-Dflyway.url=jdbc:postgresql://localhost:5432/testdb \
-Dflyway.user=postgres \
-Dflyway.password=postgres
- name: Run migrations
run: |
mvn flyway:migrate \
-Dflyway.url=jdbc:postgresql://localhost:5432/testdb \
-Dflyway.user=postgres \
-Dflyway.password=postgres
- name: Test migration info
run: |
mvn flyway:info \
-Dflyway.url=jdbc:postgresql://localhost:5432/testdb \
-Dflyway.user=postgres \
-Dflyway.password=postgres
migrate:
needs: validate
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
environment:
name: production
steps:
- uses: actions/checkout@v3
- name: Set up Java
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'adopt'
- name: Execute migrations
run: |
mvn flyway:migrate \
-Dflyway.url=${{ secrets.PROD_DB_URL }} \
-Dflyway.user=${{ secrets.PROD_DB_USER }} \
-Dflyway.password=${{ secrets.PROD_DB_PASSWORD }}
- name: Post migration info
run: |
mvn flyway:info \
-Dflyway.url=${{ secrets.PROD_DB_URL }} \
-Dflyway.user=${{ secrets.PROD_DB_USER }} \
-Dflyway.password=${{ secrets.PROD_DB_PASSWORD }}
8.2.2 GitLab CI統合例
# .gitlab-ci.yml
stages:
- validate
- migrate
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
validate:migration:
stage: validate
image: maven:3.9-eclipse-temurin-17
services:
- postgres:15
variables:
POSTGRES_DB: testdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
script:
- mvn clean flyway:validate flyway:migrate
-Dflyway.url=jdbc:postgresql://postgres:5432/testdb
-Dflyway.user=postgres
-Dflyway.password=postgres
migrate:production:
stage: migrate
image: maven:3.9-eclipse-temurin-17
only:
- main
when: manual
script:
- mvn flyway:migrate
-Dflyway.url=$PROD_DB_URL
-Dflyway.user=$PROD_DB_USER
-Dflyway.password=$PROD_DB_PASSWORD
8.3 障害対応
8.3.1 マイグレーション失敗時対応
シナリオ1: 構文エラー
-- V5__Add_column.sql(失敗)
ALTER TABLE users ADD COLUMN invalid_date; -- エラー: 型指定なし
-- 対応: V6で修正
-- V6__Fix_column_definition.sql
ALTER TABLE users ADD COLUMN event_date TIMESTAMP;
シナリオ2: 権限エラー
Error: Permission denied on table users
対応:
- 接続ユーザーの権限確認
- テーブルの所有者確認
- SELECTグラント、ALTERグラント確認
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO migration_user;
GRANT USAGE, CREATE ON SCHEMA public TO migration_user;
シナリオ3: トランザクション失敗
Error: Transaction rollback due to constraint violation
対応:
- NOT NULL制約への対応
- 一意制約への対応
- 外部キー制約への対応
-- 段階的実行
-- Step 1: デフォルト値を設定
UPDATE users SET status = 'ACTIVE' WHERE status IS NULL;
-- Step 2: 制約追加
ALTER TABLE users ADD CONSTRAINT nn_status
CHECK(status IS NOT NULL);
8.3.2 ロールバック手順
本番環境でのロールバック(必要な場合):
# 1. 現在のマイグレーション状態確認
flyway -url=$PROD_DB_URL info
# 2. テーブルから不正なレコード削除
DELETE FROM flyway_schema_history
WHERE version = '5' AND success = false;
# 3. バックアップから復元(必要な場合)
# pg_restore -d myapp backup.dump
# 4. バージョン検証
flyway -url=$PROD_DB_URL validate
# 5. 修正版マイグレーション作成
# V6__Fix_previous_error.sql を新規作成
# 6. 再度実行
flyway -url=$PROD_DB_URL migrate
9. トラブルシューティング
9.1 よくあるエラーと対応
9.1.1 "Checksum mismatch" エラー
Error: Checksum mismatch of migration V1__Create_table.sql
Calculated checksum: a1b2c3d4e5f6
Stored checksum: x9y8z7w6v5u4
原因: マイグレーションファイルが実行後に変更された
対応:
-- オプション1: ファイルを元に戻す
git checkout db/migration/V1__Create_table.sql
-- オプション2: 新しいマイグレーションで修正
-- V2__Fix_v1_changes.sql を作成
-- オプション3: チェックサムを更新(本番環境以外)
UPDATE flyway_schema_history
SET checksum = (SELECT CHECKSUM FROM ...)
WHERE version = '1';
9.1.2 "Version duplicates" エラー
Error: Detected version duplicates: V2
- V2__Feature_A.sql
- V2__Feature_B.sql
原因: 同じバージョン番号のマイグレーションが複数存在
対応:
# 1. ファイル確認
ls src/main/resources/db/migration/ | grep V2
# 2. ファイルリネーム
mv V2__Feature_B.sql V3__Feature_B.sql
# 3. Flywayコマンド再実行
flyway migrate
9.1.3 "Schema does not exist" エラー
Error: PostgreSQL JDBC Driver:
ERROR: schema "myschema" does not exist
原因: マイグレーション対象スキーマが存在しない
対応:
-- V1__Create_schema.sql
CREATE SCHEMA IF NOT EXISTS myschema;
-- または flyway.conf で設定
flyway.schemas=myschema
9.1.4 "Table 'flyway_schema_history' already exists" エラー
Error:
Flyway table 'flyway_schema_history' already exists
in database
原因: Flywayが複数回初期化された
対応:
# 1. 既存テーブル確認
SELECT * FROM flyway_schema_history;
# 2. テーブルが正常 → 無視して続行
flyway -baselineOnMigrate=true migrate
# 3. テーブル破損の場合 → 再作成
DROP TABLE IF EXISTS flyway_schema_history;
flyway migrate
9.2 パフォーマンス最適化
9.2.1 大規模マイグレーションの最適化
-- V1__Bulk_insert_optimization.sql
-- 避けるべき
-- INSERT INTO large_table VALUES (...);
-- INSERT INTO large_table VALUES (...);
-- (数百万行)
-- 推奨: COPY コマンド(PostgreSQL)
\COPY large_table FROM '/tmp/data.csv' WITH (FORMAT csv);
-- または LOAD DATA(MySQL)
LOAD DATA INFILE '/tmp/data.csv'
INTO TABLE large_table
FIELDS TERMINATED BY ',';
-- またはバルク挿入
INSERT INTO large_table SELECT * FROM staging_table;
9.2.2 インデックス作成の最適化
-- V2__Create_indexes_concurrently.sql
-- PostgreSQL: 本番テーブル以外で発生
-- 同時実行インデックス作成(ロック最小化)
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
-- MySQL: 並列処理
ALTER TABLE users ADD INDEX idx_email (email), ALGORITHM=INPLACE, LOCK=NONE;
9.3 デバッグ手法
9.3.1 ログレベルの調整
# flyway.conf
flyway.logger=slf4j
# logback.xml
<configuration>
<logger name="org.flywaydb" level="DEBUG" />
<logger name="org.flywaydb.core.internal.command.DbMigrate" level="TRACE" />
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
9.3.2 SQLスクリプト検証
マイグレーション実行前に個別検証:
# 1. スクリプトをファイルに保存
cat src/main/resources/db/migration/V1__Initial.sql > /tmp/test.sql
# 2. 直接実行
psql -h localhost -U postgres -d testdb -f /tmp/test.sql
# 3. 文法チェック(MySQLの場合)
mysql -h localhost -u root -p testdb < /tmp/test.sql
10. ベストプラクティス
10.1 マイグレーション設計
10.1.1 原子性の保証
各マイグレーションは単一の独立した変更を表現:
-- 悪い例: 複数の論理的変更を1つのマイグレーションに
-- V1__Add_users_and_posts_tables.sql
CREATE TABLE users (...);
CREATE TABLE posts (...);
ALTER TABLE posts ADD CONSTRAINT fk_user REFERENCES users(id);
ALTER TABLE posts ADD INDEX idx_user_id ON user_id;
-- 良い例: 論理的に分割
-- V1__Create_users_table.sql
CREATE TABLE users (...);
-- V2__Create_posts_table.sql
CREATE TABLE posts (...);
-- V3__Add_foreign_key_constraint.sql
ALTER TABLE posts ADD CONSTRAINT fk_user REFERENCES users(id);
-- V4__Add_indexes.sql
ALTER TABLE posts ADD INDEX idx_user_id ON user_id;
10.1.2 イディオマティックなSQLの使用
-- 良い例: 環境に応じた条件付き実行
-- V1__Create_table_if_not_exists.sql
-- PostgreSQL
CREATE TABLE IF NOT EXISTS users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE
);
-- MySQL
CREATE TABLE IF NOT EXISTS users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE
);
-- Oracle
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE users (
id NUMBER PRIMARY KEY,
username VARCHAR2(255) NOT NULL UNIQUE
)';
EXCEPTION
WHEN OTHERS THEN NULL;
END;
10.1.3 データベース固有の機能使用
-- PostgreSQL特有
-- V1__Create_enum_type.sql
CREATE TYPE status_enum AS ENUM('ACTIVE', 'INACTIVE', 'PENDING');
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
status status_enum DEFAULT 'ACTIVE'
);
-- MySQL特有
-- V1__Create_table_with_partitioning.sql
CREATE TABLE events (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
event_date DATE NOT NULL,
data JSON
) PARTITION BY RANGE(YEAR(event_date)) (
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
10.2 ドキュメンテーション
10.2.1 マイグレーション記述規則
-- V1__Create_users_table.sql
--
-- 目的: ユーザー管理テーブルの初期構築
--
-- 作成者: DB Team
-- 作成日: 2024-01-10
--
-- 説明:
-- ユーザー認証・管理用のメインテーブル
-- 以下のカラムで構成:
-- - id: ユニークな識別子(主キー)
-- - username: ユーザー名(一意)
-- - email: メールアドレス(一意)
-- - created_at: 作成日時
-- - updated_at: 更新日時
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- インデックス作成
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_username ON users(username);
10.2.2 マイグレーションログ管理
# マイグレーション実行ログの記録
flyway info > migration_log_$(date +%Y%m%d_%H%M%S).txt
# 重要な変更のみ抽出
grep "SUCCESS" migration_log_*.txt | tail -20
10.3 チーム運用
10.3.1 レビュー・チェックリスト
マイグレーション作成時のレビューポイント:
- ファイル名規則に従っているか(V番号の連続性)
- SQLは対象DBで正常に実行可能か
- データ損失の可能性がないか
- バックアップの必要性検討
- パフォーマンス影響がないか
- ロールバック方法が明確か
- ドキュメント・コメント完備か
- 他マイグレーションとの依存関係チェック
- 本番環境での実行時間推定
- アラート・モニタリング設定
10.3.2 コミット規則
# 良い例のコミットメッセージ
git commit -m "db: add user profile fields [V5]
- Add profile_bio column to users table
- Add avatar_url column to users table
- Add updated_at timestamp
- Create index on profile_bio for search
Migration: V5__Add_profile_fields_to_users.sql
Execution time (estimated): 50ms
"
10.3.3 マイグレーションの命名規則統一
チーム全体で統一した命名規則の採用:
推奨: セマンティックバージョン
V1__Create_users_table.sql
V1.1__Add_email_column_to_users.sql
V2__Create_products_table.sql
または チケット番号ベース
V1__PROJ-100_Initialize_schema.sql
V2__PROJ-101_Add_user_profile_fields.sql
10.4 本番環境運用
10.4.1 本番環境でのマイグレーション実行戦略
#!/bin/bash
# 本番環境マイグレーション実行スクリプト
set -e
DB_URL="jdbc:postgresql://prod-db:5432/myapp"
DB_USER="${PROD_DB_USER}"
DB_PASSWORD="${PROD_DB_PASSWORD}"
echo "=== マイグレーション情報確認 ==="
flyway -url=$DB_URL -user=$DB_USER -password=$DB_PASSWORD info
echo ""
echo "=== マイグレーション検証 ==="
flyway -url=$DB_URL -user=$DB_USER -password=$DB_PASSWORD validate
echo ""
echo "=== 事前チェック ==="
read -p "本番環境へのマイグレーション実行を確認してください (y/n): " confirm
if [ "$confirm" != "y" ]; then
echo "中止しました"
exit 1
fi
echo ""
echo "=== マイグレーション実行開始 ==="
START_TIME=$(date +%s)
flyway -url=$DB_URL -user=$DB_USER -password=$DB_PASSWORD migrate
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
echo ""
echo "=== マイグレーション完了 ==="
echo "実行時間: ${DURATION}秒"
echo ""
flyway -url=$DB_URL -user=$DB_USER -password=$DB_PASSWORD info
10.4.2 ロールフォワード戦略
失敗時の対応:
-- マイグレーション失敗で取り消す必要がある場合
-- (通常は新マイグレーションで修正を推奨)
-- 1. 失敗したマイグレーションを履歴から削除
DELETE FROM flyway_schema_history
WHERE version = '5' AND success = false;
-- 2. 新しいマイグレーション作成
-- V5__Fix_previous_error.sql
-- 3. 再実行
flyway migrate
まとめ
Flywayはシンプルかつ強力なデータベースマイグレーションツールです。主な要点:
- シンプルな設計: SQLファイルを配置するだけでマイグレーション実行
- バージョン管理: すべての変更履歴を自動追跡
- 安全な実行: トランザクション・検証・チェックサム機構
- 柔軟な統合: JVM・CLI・Docker・CI/CDに対応
- 本番対応: 豊富な設定オプションで様々なシナリオに対応
チーム開発では以下の点を重視:
- 統一された命名規則
- 論理的な変更単位への分割
- 十分なドキュメンテーション
- 段階的な検証・テスト
- 本番環境での慎重な実行
Flywayを正しく運用することで、データベーススキーマのバージョン管理はアプリケーションコードと同様に安全かつ予測可能になります。