DBのフォールバックとは?障害時にサービスを止めないための設計パターンと実装例

目次

「DBが落ちたら全部止まる」、それ本当に仕方ない?

深夜にSlack通知が鳴る。「本番DBが応答しません」。ユーザーには500エラーの白い画面。復旧まで30分、その間の売上はゼロ。こんな経験、あるいは想像するだけでも胃が痛くなりますよね。

でも考えてみてください。DBが落ちた時に「何も返せない」のと「キャッシュから古いデータでも返せる」のでは、ユーザー体験がまるで違います。後者の考え方が、フォールバック(fallback)です。

本記事では、DBフォールバックの基本概念から、実務で使える3つの実装パターン、やってはいけないアンチパターンまでを解説します。

DBのフォールバックとは? ― 障害時の「代替手段」で動き続ける仕組み

フォールバック(fallback)は、「fall(落ちる)」+「back(後ろに)」で、「本来の手段が使えない時に、代替手段に切り替える」という意味です。

【フォールバックなし】
リクエスト → DB問い合わせ → DB障害 → 500エラー → ユーザーに白い画面

【フォールバックあり】
リクエスト → DB問い合わせ → DB障害 → キャッシュから返す → ユーザーには古いけどデータが見える

完璧なデータを返せなくても、「何かしら返す」ことでサービスの体験を維持するのがフォールバックの目的です。100点が無理なら80点でいい、0点だけは避ける、という設計思想です。

どんな場面で必要?

フォールバックが効くのは、「多少古い・不完全なデータでもユーザーに価値がある」場面です。

場面 フォールバック 理由
商品一覧ページ ✅ 有効 5分前のデータでも表示できれば離脱を防げる
ダッシュボードの集計値 ✅ 有効 リアルタイムでなくても直近の値で十分判断できる
決済処理 ❌ 不適切 古いデータで決済すると二重課金や金額不整合のリスク
在庫数チェック ⚠️ 要検討 古い在庫数で受注すると欠品になるが、表示だけなら許容範囲

ポイントは、「読み取り」にはフォールバックが効きやすく、「書き込み」には慎重であるべきということ。決済や在庫確定のような整合性が必須の処理は、フォールバックではなくリトライやキューイングで対応するのが正解です。

フォールバックの3つの実装パターン

パターン1:キャッシュフォールバック(最も一般的)

正常時にキャッシュを更新しておき、DB障害時にはキャッシュから返す。Redisやインメモリキャッシュとの組み合わせが定番です。

// キャッシュフォールバックの基本パターン
import Redis from 'ioredis';
import { Pool } from 'pg';

const redis = new Redis();
const db = new Pool();

async function getProducts(): Promise {
  try {
    // 1. まずDBから最新データを取得
    const result = await db.query('SELECT * FROM products WHERE active = true');
    const products = result.rows;

    // 2. 成功したらキャッシュを更新(TTL 5分)
    await redis.set('products:active', JSON.stringify(products), 'EX', 300);

    return products;
  } catch (error) {
    console.error('DB query failed, falling back to cache:', error);

    // 3. DB障害時はキャッシュから返す
    const cached = await redis.get('products:active');
    if (cached) {
      return JSON.parse(cached);
    }

    // 4. キャッシュもなければエラー
    throw new Error('DB and cache both unavailable');
  }
}

この実装のポイントは、正常時に毎回キャッシュを更新していること。DB障害が起きた時にキャッシュが空だと意味がないので、「常にキャッシュを温めておく」のが重要です。

パターン2:リードレプリカフォールバック

プライマリDBが落ちた時に、リードレプリカ(読み取り専用の複製DB)に切り替えるパターン。キャッシュと違いデータの鮮度がほぼリアルタイムなのがメリットです。

// リードレプリカフォールバック
const primaryDb = new Pool({ host: 'db-primary.example.com' });
const replicaDb = new Pool({ host: 'db-replica.example.com' });

async function queryWithFallback(sql: string, params?: any[]) {
  try {
    return await primaryDb.query(sql, params);
  } catch (error) {
    console.warn('Primary DB failed, switching to replica:', error);
    return await replicaDb.query(sql, params);
  }
}

AWS RDSやAurora、Cloud SQLなど主要なマネージドDBサービスはリードレプリカを簡単に追加できます。コスト面でレプリカを常時稼働させる余裕があるなら、最も信頼性の高いフォールバック手段です。

パターン3:静的データフォールバック(最後の砦)

DBもキャッシュも死んだ場合の最終防衛線。JSONファイルやCDNに置いた静的データを返します。

// 静的データフォールバック(最後の砦)
import fallbackData from './fallback/products.json';

async function getProductsSafe(): Promise {
  try {
    return await getProductsFromDB();
  } catch {
    try {
      return await getProductsFromCache();
    } catch {
      // DB・キャッシュ全滅 → 静的データで最低限の表示を維持
      console.error('All sources failed, returning static fallback');
      return fallbackData;
    }
  }
}

ECサイトなら「定番商品だけでも表示する」、SaaSなら「メンテナンス中の案内ページを出す」など、0点を避けるための最低限のデータを用意しておくイメージです。

やってはいけないアンチパターン

アンチパターン1:書き込み処理にフォールバックを適用する

「DBが落ちてるからキャッシュに書いておこう」は非常に危険です。DB復旧後にキャッシュとDBの整合性が取れなくなり、データ不整合が連鎖します。書き込みが失敗する場合は、メッセージキュー(SQS、RabbitMQ等)に入れて後で再処理するのが安全なパターンです。

アンチパターン2:フォールバックデータであることを隠す

キャッシュや静的データを返している時に、ユーザーに何も伝えないのはNGです。「最終更新: 5分前のデータを表示しています」のような表示を出しておけば、ユーザーはデータの鮮度を判断できます。古いデータを最新だと誤認させるのが一番まずい

アンチパターン3:フォールバックのテストをしていない

フォールバックは障害時にしか動かないコードなので、テストされないまま放置されがちです。いざ障害が起きた時に「フォールバック先のキャッシュが空だった」「接続設定が古くて繋がらない」では本末転倒。定期的に意図的にDBへの接続を切ってフォールバックが動くか確認しましょう。

DB障害対策をさらに深める関連記事

フォールバックの設計を理解したら、DB運用やインフラ設計の周辺知識も固めておきましょう。

データベース・インフラ

設計・運用

まとめ:100点が無理なら80点を返す、0点だけは避ける

本記事のポイントを整理します。

  • フォールバックとは、DB障害時に代替手段(キャッシュ・レプリカ・静的データ)でサービスを継続する設計パターン
  • 読み取り処理に有効。書き込みにはフォールバックではなくキューイングで対応
  • 3つのパターン:キャッシュフォールバック(定番)、リードレプリカ(高信頼)、静的データ(最後の砦)
  • アンチパターンに注意:書き込みへの適用、データ鮮度の非表示、テスト不足

DBは落ちます。どんなに堅牢な構成でも、ゼロダウンタイムは幻想に近い。大事なのは「落ちない」ことではなく、「落ちた時にどう振る舞うか」を事前に設計しておくこと。フォールバックは、その設計の第一歩です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次