database
A
信頼度ランク
| S | 公式ソース確認済み |
| A | 成功実績多数・失敗例少数 |
| B | 賛否両論 |
| C | 動作未確認・セキュリティリスク高 |
| Z | 個人所感 |
Redisをキャッシュだけに使うのはもったいない:高度な使い方
Redisのデータ構造(List・Set・Sorted Set・Hash・Stream)を活用した高度なユースケースを解説。セッション管理・レート制限・リアルタイムランキング・メッセージキューなどの実装例を紹介します。
一言結論
RedisはSorted Setでリアルタイムランキング、Streamでメッセージキュー、LUAスクリプトでアトミックなレート制限を実装できる汎用データ構造サーバーであり、「キャッシュ専用」と割り切るのはその能力の半分以下しか使っていない。
Redis のデータ構造
Redis は「高速なデータ構造サーバー」です。キャッシュ以外にも多くのユースケースがあります。
| データ構造 | コマンド例 | 使いどころ |
|---|---|---|
| String | SET・GET・INCR | キャッシュ・カウンター・フラグ |
| List | LPUSH・RPOP・LRANGE | キュー・スタック |
| Set | SADD・SMEMBERS・SINTER | タグ・ユニーク集合・集合演算 |
| Sorted Set | ZADD・ZRANGE・ZRANK | ランキング・スコアボード |
| Hash | HSET・HGET・HMGET | オブジェクト・ユーザーセッション |
| Stream | XADD・XREAD | イベントログ・メッセージキュー |
ユースケース1: セッション管理
import { createClient } from "redis";
const redis = createClient();
// セッションの保存(30分有効)
async function saveSession(sessionId: string, userId: string, data: object) {
await redis.hSet(`session:${sessionId}`, {
userId,
...data,
createdAt: Date.now().toString(),
});
await redis.expire(`session:${sessionId}`, 1800); // 30分
}
// セッションの取得
async function getSession(sessionId: string) {
const session = await redis.hGetAll(`session:${sessionId}`);
if (!session.userId) return null;
// アクセスのたびに有効期限を延長(スライディングセッション)
await redis.expire(`session:${sessionId}`, 1800);
return session;
}
ユースケース2: レート制限(固定ウィンドウ)
async function rateLimit(
ip: string,
limit: number = 100,
windowSecs: number = 60
): Promise<{ allowed: boolean; remaining: number }> {
const key = `rate:${ip}:${Math.floor(Date.now() / 1000 / windowSecs)}`;
const count = await redis.incr(key);
if (count === 1) {
await redis.expire(key, windowSecs);
}
return {
allowed: count <= limit,
remaining: Math.max(0, limit - count),
};
}
// ミドルウェア
app.use(async (req, res, next) => {
const { allowed, remaining } = await rateLimit(req.ip);
res.set("X-RateLimit-Remaining", remaining.toString());
if (!allowed) return res.status(429).json({ error: "Too Many Requests" });
next();
});
ユースケース3: リアルタイムランキング(Sorted Set)
// スコアを追加・更新
await redis.zAdd("leaderboard", { score: 1500, value: "user:alice" });
await redis.zAdd("leaderboard", { score: 1200, value: "user:bob" });
await redis.zAdd("leaderboard", { score: 1800, value: "user:charlie" });
// 上位10名を取得(高スコア順)
const top10 = await redis.zRangeWithScores("leaderboard", 0, 9, { REV: true });
// → [{ value: "user:charlie", score: 1800 }, ...]
// ユーザーのランクを取得(0始まり)
const rank = await redis.zRevRank("leaderboard", "user:alice");
// → 1(2位)
// スコアをインクリメント
await redis.zIncrBy("leaderboard", 100, "user:alice"); // 1600点に
ユースケース4: シンプルなジョブキュー(List)
// プロデューサー: タスクをエンキュー
async function enqueue(queueName: string, task: object) {
await redis.lPush(queueName, JSON.stringify(task));
}
// コンシューマー: タスクを処理(ブロッキング)
async function processQueue(queueName: string) {
while (true) {
// 最大10秒待って取得(ブロッキング)
const result = await redis.brPop(queueName, 10);
if (!result) continue;
const task = JSON.parse(result.element);
await processTask(task);
}
}
// 使用例
await enqueue("email_queue", { to: "user@example.com", subject: "Welcome!" });
ユースケース5: Pub/Sub でリアルタイム通知
// パブリッシャー
await redis.publish("notifications", JSON.stringify({
type: "new_message",
userId: "123",
content: "こんにちは!",
}));
// サブスクライバー
const subscriber = redis.duplicate();
await subscriber.connect();
await subscriber.subscribe("notifications", (message) => {
const data = JSON.parse(message);
// WebSocket でクライアントに転送
wsClients.get(data.userId)?.send(message);
});
ユースケース6: 分散ロック(Redlock)
import Redlock from "redlock";
const redlock = new Redlock([redis], {
retryCount: 3,
retryDelay: 200,
});
async function processPayment(orderId: string) {
// 同じ注文を同時に処理しないようにロック
const lock = await redlock.acquire([`lock:order:${orderId}`], 5000); // 5秒
try {
await processOrderPayment(orderId);
} finally {
await lock.release();
}
}
TTL の設計原則
// TTL を設定しないと Redis がメモリ不足になる
// すべての一時データには必ず TTL を設定する
await redis.set("cache:user:123", JSON.stringify(user), { EX: 300 }); // 5分
// maxmemory-policy の設定(redis.conf)
// allkeys-lru: メモリ不足時に最近使われていないキーを削除(キャッシュ用途)
// noeviction: メモリ不足でもエラーを返す(永続データ用途)
まとめ
| ユースケース | データ構造 | コマンド |
|---|---|---|
| キャッシュ | String | SET・GET・EX |
| セッション | Hash | HSET・HGETALL |
| レート制限 | String | INCR・EXPIRE |
| ランキング | Sorted Set | ZADD・ZRANGE |
| キュー | List | LPUSH・BRPOP |
| 通知 | Pub/Sub | PUBLISH・SUBSCRIBE |
| 分散ロック | String | Redlock ライブラリ |