キャッシュ無効化
キャッシュされたデータが古くなった際に、最新のデータに更新または削除する仕組み
キャッシュ無効化とは
キャッシュ無効化 (Cache Invalidation) は、キャッシュされたデータがソースデータと乖離した際に、キャッシュを最新の状態に更新または削除する仕組みである。Phil Karlton の名言「コンピュータサイエンスで難しいことは 2 つだけ。キャッシュの無効化と命名だ」が示すとおり、正しく実装するのが最も困難なキャッシュの課題だ。
無効化戦略の比較
| 戦略 | 仕組み | 整合性 | 複雑さ |
|---|---|---|---|
| TTL | 有効期限で自動削除 | TTL 内は古いデータ | 低い |
| Write-Through | 書き込み時にキャッシュも更新 | 常に最新 | 中程度 |
| Write-Behind | 書き込みをキャッシュに行い、非同期で DB に反映 | 最新だが DB 遅延あり | 高い |
| イベント駆動 | データ更新イベントでキャッシュを無効化 | ほぼ最新 | 中程度 |
| 手動パージ | 明示的にキャッシュを削除 | 操作時のみ最新 | 低い |
TTL ベースの無効化
最もシンプルで広く使われる。キャッシュに有効期限を設定し、期限切れで自動削除する。
// Redis でのキャッシュ + TTL
async function getUser(userId: string): Promise<User> {
const cached = await redis.get(`user:${userId}`);
if (cached) return JSON.parse(cached);
const user = await db.users.findById(userId);
await redis.setex(`user:${userId}`, 300, JSON.stringify(user)); // 5分 TTL
return user;
}
TTL 内は古いデータが返る可能性がある。許容できる古さ (Staleness) に応じて TTL を設定する。
イベント駆動の無効化
DynamoDB Streams + Lambda でキャッシュを即座に無効化する。
CloudFront のキャッシュ無効化
デプロイ時に aws cloudfront create-invalidation --paths "/*" で全キャッシュを無効化するのが一般的だ。ただし、インバリデーションはエッジロケーション全体に伝播するまで数分かかる。
より良いアプローチは、コンテンツハッシュをファイル名に含めること (app.a1b2c3.js)。ファイル内容が変わればハッシュが変わるため、インバリデーション不要で即座に新しいファイルが配信される。
よくある失敗パターン
キャッシュとDBの不整合
DB を更新してからキャッシュを更新するまでの間に、別のリクエストが古いキャッシュを読む。対策: キャッシュを「更新」ではなく「削除」する (Cache-Aside パターン)。次のリクエストでキャッシュミスが発生し、DB から最新データを取得する。
Thundering Herd
人気のあるキャッシュキーが TTL 切れになると、大量のリクエストが同時に DB に殺到する。対策: キャッシュの TTL にジッター (ランダムな揺らぎ) を加え、同時に期限切れになるのを防ぐ。
キャッシュ無効化の背景や設計思想は関連書籍に詳しい。