ストーリー
高橋アーキテクトが図を描いた。
4層のキャッシュ
全体像
graph TD
User["ユーザー"]
L1["Layer 1: ブラウザキャッシュ<br/>Cache-Control, ETag"]
L2["Layer 2: CDNキャッシュ<br/>CloudFront, Cloudflare"]
L3["Layer 3: アプリケーションキャッシュ<br/>Redis, Memcached"]
L4["Layer 4: データベースキャッシュ<br/>クエリキャッシュ、バッファプール"]
DB[("データベース")]
User -->|"最速"| L1
L1 --> L2
L2 --> L3
L3 --> L4
L4 -->|"最も遅い"| DB
classDef fast fill:#cfc,stroke:#393
classDef slow fill:#fcc,stroke:#933
class L1 fast
class DB slow
Layer 1: ブラウザキャッシュ
ユーザーのブラウザにデータを保存します。ネットワークリクエストが発生しないため最速です。
// HTTPヘッダーでブラウザキャッシュを制御
class CacheHeaderMiddleware {
// 静的アセット: 長期キャッシュ + キャッシュバスティング
staticAssets(res: Response): void {
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
// 1年間キャッシュ。ファイル名にハッシュを含めて更新を管理
// 例: style.abc123.css
}
// API レスポンス: 短期キャッシュ + 再検証
apiResponse(res: Response): void {
res.setHeader('Cache-Control', 'private, max-age=60, must-revalidate');
res.setHeader('ETag', '"abc123"');
// 60秒キャッシュ。期限後はETagで変更確認
}
// 動的コンテンツ: キャッシュしない
dynamicContent(res: Response): void {
res.setHeader('Cache-Control', 'no-store');
// 決済ページ、個人情報ページなど
}
}
| ヘッダー | 用途 |
|---|---|
Cache-Control: public | CDNでもキャッシュ可能 |
Cache-Control: private | ブラウザのみキャッシュ可能 |
max-age=N | N秒間キャッシュを有効にする |
ETag | コンテンツのバージョン識別子 |
no-store | キャッシュしない |
Layer 2: CDNキャッシュ
CDN(Content Delivery Network)のエッジサーバーにコンテンツをキャッシュします。
// CDN設定の例
const cdnConfig = {
// 静的アセット: 長期キャッシュ
staticAssets: {
pathPattern: '/assets/*',
ttl: 86400 * 365, // 1年
behaviors: {
compress: true,
minTTL: 86400,
},
},
// API レスポンス: 短期キャッシュ
apiResponses: {
pathPattern: '/api/products/*',
ttl: 60, // 1分
behaviors: {
queryStringCaching: 'all', // クエリ文字列もキーに含める
headerWhitelist: ['Accept-Language'],
},
},
// 動的ページ: キャッシュしない
dynamicPages: {
pathPattern: '/api/cart/*',
ttl: 0,
behaviors: {
forwardCookies: 'all',
},
},
};
Layer 3: アプリケーションキャッシュ
RedisやMemcachedを使ったサーバーサイドのキャッシュです。
// Redis を使ったアプリケーションキャッシュ
class ApplicationCache {
constructor(private redis: RedisClient) {}
// クエリ結果のキャッシュ
async getCachedQuery<T>(
queryKey: string,
fetchFn: () => Promise<T>,
ttlSeconds: number = 300
): Promise<T> {
const cached = await this.redis.get(queryKey);
if (cached) {
return JSON.parse(cached);
}
const result = await fetchFn();
await this.redis.setex(queryKey, ttlSeconds, JSON.stringify(result));
return result;
}
// 計算結果のキャッシュ
async getCachedComputation<T>(
key: string,
computeFn: () => Promise<T>,
ttlSeconds: number = 600
): Promise<T> {
return this.getCachedQuery(key, computeFn, ttlSeconds);
}
}
// 使用例
const products = await cache.getCachedQuery(
'popular-products:page1',
() => db.getPopularProducts({ page: 1, limit: 20 }),
300 // 5分間キャッシュ
);
Layer 4: データベースキャッシュ
データベース自身が持つキャッシュ機構です。
// PostgreSQLの場合
// shared_buffers: メモリ上にデータページをキャッシュ
// effective_cache_size: OSのファイルシステムキャッシュも含めた推定値
// クエリプラン最適化のためのヒント
const dbConfig = {
sharedBuffers: '4GB', // DB専用キャッシュ
effectiveCacheSize: '12GB', // OS含む全キャッシュ
workMem: '256MB', // ソート・ハッシュ用
};
各層の特性比較
| 層 | レイテンシ | 容量 | 管理者 | 適したデータ |
|---|---|---|---|---|
| ブラウザ | 0ms | 小 | フロントエンド | 静的アセット、UIデータ |
| CDN | 1-50ms | 大 | インフラ | 静的ファイル、公開APIレスポンス |
| アプリケーション | 1-10ms | 中 | バックエンド | クエリ結果、セッション |
| データベース | 1-5ms | 中 | DBA | クエリプラン、データページ |
まとめ
| ポイント | 内容 |
|---|---|
| 多層構造 | ブラウザ → CDN → アプリ → DB の4層 |
| 原則 | ユーザーに近い層でキャッシュするほど高速 |
| ブラウザ | Cache-Control, ETag でHTTPキャッシュ |
| CDN | エッジサーバーで地理的に分散 |
| アプリケーション | Redis/Memcached でクエリ結果をキャッシュ |
| データベース | バッファプール、クエリキャッシュ |
チェックリスト
- 4層のキャッシュアーキテクチャを説明できる
- 各層の適したデータとレイテンシを理解した
- Cache-Controlヘッダーの基本的な使い方を把握した
- CDNキャッシュの役割を理解した
次のステップへ
次は「キャッシュの無効化」を学びます。“コンピュータサイエンスで最も難しい問題の1つ”と言われるキャッシュ無効化の戦略を理解しましょう。
推定読了時間: 30分