目次

読了時間: 約8分 | 文字数: 約3,100字

「コンピュータサイエンスには2つの難問しかない。キャッシュの無効化と命名だ」——Phil Karlton のこの言葉は、キャッシュの重要性と難しさを端的に表している。適切なキャッシング戦略は応答時間を100分の1に短縮し、不適切な戦略はユーザーに古いデータを見せ続ける。本稿では、多層キャッシュの設計指針を整理する。

キャッシュの階層構造

Web アプリケーションにおけるキャッシュは、ユーザーに近い順に積み重なる。

ブラウザキャッシュ → CDN → リバースプロキシ → アプリケーションキャッシュ → データベースキャッシュ

上位のキャッシュがヒットすれば、下位への問い合わせは発生しない。この多層構造の設計がパフォーマンスの鍵だ。

ブラウザキャッシュ

最もユーザーに近いキャッシュ。HTTP ヘッダーで制御する。

Cache-Control ヘッダー

Cache-Control: public, max-age=31536000, immutable
  • public: CDN もキャッシュ可能
  • max-age=31536000: 1年間有効
  • immutable: コンテンツが変わらないことを宣言(再検証リクエストも送らない)

ETag と条件付きリクエスト

# レスポンス
ETag: "abc123"

# 次回リクエスト
If-None-Match: "abc123"
→ 変更なし: 304 Not Modified(ボディなし)
→ 変更あり: 200 OK(新しいコンテンツ)

コンテンツが変わっていなければ転送量ゼロ。帯域幅とサーバー負荷を大幅に削減できる。

CDN キャッシュ

CDN(Content Delivery Network)は世界中のエッジサーバーにコンテンツをキャッシュする。ユーザーから地理的に近いサーバーがレスポンスを返すため、レイテンシが低い。

静的アセットの戦略

CSS、JavaScript、画像ファイルには、ファイル名にハッシュを含めて Cache-Control: immutable を設定する。

styles.a1b2c3.css  → Cache-Control: max-age=31536000, immutable

コンテンツが変わればハッシュが変わり、新しい URL になるため、キャッシュ無効化の問題が発生しない。

動的コンテンツの戦略

API レスポンスのキャッシュは短時間にする。max-age=60 で1分間キャッシュし、stale-while-revalidate=300 でバックグラウンド更新を許可する。

アプリケーションキャッシュ(Redis)

Redis はインメモリデータストアで、ミリ秒単位の応答速度を提供する。

キャッシュアサイドパターン

最も一般的なキャッシュパターン。

def get_user(user_id):
    # 1. キャッシュを確認
    cached = redis.get(f"user:{user_id}")
    if cached:
        return json.loads(cached)
    
    # 2. キャッシュミス → DB から取得
    user = db.query("SELECT * FROM users WHERE id = %s", user_id)
    
    # 3. キャッシュに保存(TTL 300秒)
    redis.setex(f"user:{user_id}", 300, json.dumps(user))
    return user

ライトスルーパターン

書き込み時にキャッシュと DB を同時に更新する。読み取り時は常にキャッシュがヒットする。書き込みは遅くなるが、読み取りが圧倒的に多いシステムに有効。

ライトバックパターン

キャッシュにのみ書き込み、一定間隔で DB に反映する。書き込み性能は最高だが、キャッシュ障害時にデータを失うリスクがある。

キャッシュ無効化の戦略

キャッシュの真の難しさは無効化にある。

TTL(Time to Live)

一定時間後にキャッシュを自動的に期限切れにする。最もシンプルだが、更新から TTL 期間だけ古いデータが表示される。

イベント駆動無効化

データが更新されたとき、対応するキャッシュキーを明示的に削除する。リアルタイム性は高いが、「どのキャッシュを消すべきか」の管理が複雑。

キャッシュスタンピード防止

TTL 切れの瞬間に大量のリクエストが同時に DB を叩く「スタンピード」問題。ロック機構や確率的早期有効期限切れで対処する。

So What?——実務への応用

  • 静的アセットはハッシュ付きファイル名 + immutable: キャッシュ無効化の問題を設計で回避する
  • API レスポンスは短い TTL + stale-while-revalidate: 鮮度とパフォーマンスを両立する
  • Redis はキャッシュアサイドから始める: パターンが最もシンプルで、問題が起きても DB が正であるため安全
  • キャッシュヒット率を監視する: 90% 以下ならキャッシュ戦略の見直しが必要。キーの設計や TTL が適切か検証する

キャッシュは「速くする」ための技術であると同時に、「壊さない」ための注意深い設計が求められる技術だ。何をキャッシュし、いつ無効化するかを明確にすることが、信頼性の高いシステムの基盤となる。

参考リンク

免責事項 — 掲載情報は執筆時点のものです。料金・機能は変更される場合があります。最新情報は各公式サイトをご確認ください。