キャッシュ戦略
ルール
- すべてのレイヤーでキャッシュ:ブラウザ(Cache-Control)、CDN(エッジ)、アプリケーション(Redis)、データベース(クエリキャッシュ)
- 多少古くなってもよいコンテンツには
stale-while-revalidate を使用 —— 高速に配信し、バックグラウンドでリフレッシュ
- Cache-Control ヘッダー:ハッシュ化された静的アセットには
public, max-age=31536000, immutable、動的ページには public, s-maxage=60, stale-while-revalidate=300
- Redis キャッシュ:JSON にシリアライズし、すべてのキーに TTL を設定、名前空間付きキーを使用(
cache:users:123)
- React Query / TanStack Query:
staleTime(データが新鮮である期間)と gcTime(メモリに保持する期間)を設定
- 書き込み時に無効化 —— データが変更されたら、キャッシュエントリを即座に削除または更新
- キャッシュアサイドパターンを使用:まずキャッシュを確認し、見つからなければソースから取得し、キャッシュに書き込む
- Next.js の場合:ISR(
revalidate)、unstable_cache、およびサーバーコンポーネントのフェッチキャッシュを活用
- タグベースの無効化:エンティティタイプごとにキャッシュエントリにタグを付け、ミューテーション時にそのタグのすべてのエントリを無効化
パターン
async function getCachedUser(id: string) {
const cached = await redis.get(`user:${id}`);
if (cached) return JSON.parse(cached);
const user = await db.user.findUnique({ where: { id } });
await redis.set(`user:${id}`, JSON.stringify(user), "EX", 3600);
return user;
}
避けるべきこと
- TTL なしのキャッシュ —— 古いデータが永久に残り続ける
- 共有 CDN キャッシュへのユーザー固有データのキャッシュ ——
private または Vary ヘッダーを使用
- キャッシュスタンピード:
stale-while-revalidate やロックを使用して群集崩壊を防ぐ
- エラーレスポンスのキャッシュ —— 成功した結果のみをキャッシュ