メインコンテンツへ移動
JA
ホーム / 技術ブログ / Expand–ContractでDBを無停止変更する
2026年7月04日 · Z-SOFT Admin · 読了目安 5 分

Expand–ContractでDBを無停止変更する

旧版と新版が同時に動ける互換手順で、DBスキーマを無停止変更します。

記事一覧へ戻る

要点: 安全な変更では、古いアプリケーションと新しいアプリケーションが一定時間共存すると考えます。

この記事の用語: Expand–Contract:新構造を追加して移行後に旧構造を削除する手順。Backfill:過去データを新構造へ補う処理。

現場の課題

列名の変更や厳しい制約を一度に入れると旧版が停止します。大きな更新は長時間ロックを保持するため、変更を戻せる小さな段階へ分ける必要があります。

単純な対策だけでは不十分な理由

デプロイ中は旧版と新版が共存します。Expand–Contractは、追加、互換書き込み、Backfill、読み取り切替、旧構造削除へ分け、各段階を戻せるようにします。

処理フロー

Expand
互換スキーマ追加
→Migrate
Dual 読み取り・書き込み
→Verify
Backfillと照合
→Contract
旧経路削除
互換性を先に追加し、旧経路が不要と証明してから削除します。

アーキテクチャ上の判断

Additive Expand

RenameはAdd、Copy、切替、Removeに分解します。

再開可能Backfill

安定キー Range、小Commit、Checkpointを使います。

制約を別途Validate

失敗扱いingを抑えて既存Rowを検証します。

さらに深く考える

ロールバックはデータ問題

新形式だけのデータがある間はReverse Compatibilityを維持します。

制約で保証

照合後にDB 制約で前提を強制します。

実装手順

  1. 現行動作を変えずに、NULL許容列または新テーブルを追加します。
  2. 旧版と新版の両方で動くコードをデプロイし、再開可能なバッチでBackfillします。
  3. フィーチャーフラグで読み取りを切り替え、ロールバック不要を確認した後のリリースで旧構造を削除します。

コード例: 再開可能なPrimary-キー Backfill

lastId = checkpoint.load('account_region_v2')
rows = SELECT id FROM accounts WHERE id > :lastId ORDER BY id LIMIT 1000
UPDATE accounts SET region_v2 = derive(region) WHERE id IN (:rows)
checkpoint.save(MAX(rows.id))
sleep(rateLimit)

各バッチ後にレプリカ遅延とLock Waitを監視し、顧客影響前に自動停止します。

想定しておく障害

  • 旧ワーカーがLegacy Fieldだけを書きます。
  • Backfillが新しいOnline 書き込みを上書きします。
  • 旧Column削除がレポートを壊します。

監視すべきこと

指標何が分かるか
旧・新読み取り/書き込み数Compatibility利用を確認します。
Backfill進捗・Mismatch完全性と変換精度です。
Lock Wait・レプリカ Lag顧客トラフィックを保護します。

設計の検証方法

  • 旧・新バージョンを同時実行します。
  • 任意CheckpointでBackfillを再開します。
  • 同時Updateを上書きしないか確認します。

本番導入の進め方

Expand、互換アプリケーション、Backfill、切替、Contractを別リリースにします。少数テナントから読み取りを切り替え、ロールバック期間と監査結果を確認してから旧構造を削除します。

本番前チェックリスト

  • 実Volume相当でDDL Lockを確認します。
  • Backfillにレート制限、Checkpoint、一時停止を持たせます。
  • 旧経路Usageゼロを確認してContractします。

まとめ

無停止マイグレーションは、賢いSQL一文ではなく、互換性があり戻せる変更の連続です。

Z-SOFTとの協業

今日のテクノロジーに関する一つひとつの判断が、その後の何年にも影響を与えます。

現状を評価し、目標を明確にし、企業の成長方針に合ったアーキテクチャを選定するために、Z-SOFTのチームにご相談ください。

相談を予約する