キャッシュ戦略の基礎 - Redis vs Memcached
📚 概要
キャッシュは、アプリケーションのパフォーマンスを劇的に向上させる重要な技術です。データベースへの繰り返しクエリを削減し、レスポンス時間を短縮します。
この記事では、最も人気のあるキャッシュソリューション Redis と Memcached について、歴史的背景から実践的な使い分けまで解説します。
🕰️ 歴史的背景
キャッシュの誕生
キャッシュという概念は、1960年代のメインフレームコンピュータの時代に遡ります。当時、CPUとメモリの速度差を埋めるために、高速な小容量メモリ(キャッシュメモリ)が導入されました。
Webアプリケーションとキャッシュ
2000年代初頭、Web 2.0の時代になると、動的なWebアプリケーションが増加しました。データベースへのアクセスがボトルネックとなり、分散キャッシュの需要が高まりました。
Memcached の誕生(2003年)
- 開発者: Brad Fitzpatrick(LiveJournal創業者)
- 背景: LiveJournalのデータベース負荷を軽減するために開発
- 特徴: シンプルで高速なkey-valueストア
Redis の誕生(2009年)
- 開発者: Salvatore Sanfilippo
- 背景: リアルタイムWebアプリケーションのニーズに応えるため
- 特徴: より多機能なデータ構造をサポート
🔧 技術解説
キャッシュの基本概念
フロー図:キャッシュヒットとミスの流れ
graph LR
A[Client] --> B{Cache Check}
B -->|Hit| C[Return from Cache]
B -->|Miss| D[Query Database]
D --> E[Store in Cache]
E --> F[Return Data]
C --> G[Fast Response]
F --> G
style C fill:#51cf66
style D fill:#ff6b6b- Cache Hit: キャッシュにデータがある場合(1-5ms)
- Cache Miss: キャッシュになく、DBクエリが必要(10-100ms)
Redis vs Memcached 比較表
| 特徴 | Redis | Memcached |
|---|---|---|
| データ構造 | String, List, Set, Hash, Sorted Set | String のみ |
| 永続化 | RDB, AOF | なし(メモリのみ) |
| レプリケーション | Master-Slave | なし |
| トランザクション | あり | なし |
| パブサブ | あり | なし |
| Lua スクリプト | あり | なし |
| メモリ効率 | やや劣る | 優れている |
| マルチスレッド | シングルスレッド | マルチスレッド |
Redis の特徴
1. 豊富なデータ構造
# String
redis.set("user:1000:name", "Alice")
# Hash
redis.hset("user:1000", mapping={
"name": "Alice",
"email": "alice@example.com",
"age": 30
})
# List
redis.lpush("recent:logs", "User login at 10:00")
# Sorted Set
redis.zadd("leaderboard", {"Alice": 1000, "Bob": 950})
2. 永続化
永続化の仕組み
graph TB
A[Redis Server] --> B{Persistence Mode}
B --> C[RDB Snapshot]
B --> D[AOF Log]
C --> E[Save to Disk Periodically]
D --> F[Record All Write Operations]
style C fill:#4dabf7
style D fill:#74c0fc- RDB: 定期的にスナップショットを保存
- AOF: 全ての書き込み操作をログとして記録
Memcached の特徴
1. シンプルさと高速性
# Simple key-value operations
memcache.set("session:abc123", {"user_id": 1000}, time=3600)
value = memcache.get("session:abc123")
2. マルチスレッド
Memcachedはマルチスレッドで動作し、複数のCPUコアを効率的に利用できます。
💡 実践例
ユースケース1: APIレスポンスのキャッシュ
// Redis-based API cache
async function getUserData(userId: number) {
const cacheKey = `user:${userId}`;
// 1. Check cache
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
// 2. Cache miss - fetch from DB
const user = await db.query("SELECT * FROM users WHERE id = $1", [userId]);
// 3. Store in cache with TTL
await redis.setex(cacheKey, 3600, JSON.stringify(user));
return user;
}
ユースケース2: セッション管理
// Memcached for session management
app.post("/login", async (c) => {
const { username, password } = await c.req.json();
// Authenticate
const user = await authenticate(username, password);
// Generate session ID
const sessionId = generateSessionId();
// Store in Memcached with TTL: 24h
await memcached.set(`session:${sessionId}`, {
userId: user.id,
username: user.username
}, 86400);
return c.json({ sessionId });
});
📊 パフォーマンス比較
10,000リクエストの処理
パフォーマンス差の図
graph TB
subgraph NoCache[Without Cache]
A1[10000 requests] --> B1[DB Query x3]
B1 --> C1[Total: 300s]
end
subgraph WithCache[With Redis Cache]
A2[10000 requests] --> B2[Redis Cache]
B2 --> C2[Total: 10s]
end
style C1 fill:#ff6b6b
style C2 fill:#51cf66結果: キャッシュ導入で 30倍 の速度改善!
- キャッシュなし: 300秒(各リクエスト30ms)
- Redisキャッシュ: 10秒(各リクエスト1ms)
🎯 使い分けガイド
Redis を選ぶべき場合
- ✅ データ永続化が必要
- ✅ 複雑なデータ構造を扱う(List, Set, Hash等)
- ✅ レプリケーションやフェイルオーバーが必要
- ✅ パブサブ機能を使いたい
- ✅ Lua スクリプトで複雑な処理を実行したい
Memcached を選ぶべき場合
- ✅ シンプルな key-value キャッシュで十分
- ✅ メモリ効率を最大化したい
- ✅ マルチスレッドのパフォーマンスが必要
- ✅ セッション管理のみの用途
🔍 関連する問題
この記事に関連するクイズ問題:
- Q1: APIで各リクエストがDBクエリを3回実行。10,000req/s処理するボトルネック解消策は?
- Q7: 低カーディナリティカラムのインデックス問題は?
- Q31: Dockerの主なメリットは?
📝 まとめ
- キャッシュ は、データベース負荷を削減し、レスポンス時間を劇的に改善する
- Redis は多機能で永続化に対応し、複雑なユースケースに最適
- Memcached はシンプルで高速、純粋なキャッシュ用途に最適
- 適切な選択 は、要件(永続化、データ構造、パフォーマンス)に基づいて行う
次のステップ: 実際のプロジェクトにRedisまたはMemcachedを導入し、パフォーマンスを測定してみましょう!