Redis 接続ガイド
アプリケーションから Azure Cache for Redis に接続する方法を説明します。
概要
Redis は、キャッシュ、セッション管理、メッセージキュー、リアルタイム分析などに使用される高速なインメモリデータストアです。
Redis 構成: ホスト: redis-{tenant}-{env}.redis.cache.windows.net、ポート: 6380 (SSL)、SSL: 必須、SKU: Basic / Standard / Premium(設定による)
用途: キャッシュ、セッション管理、レート制限、メッセージキュー、リーダーボード
Redis の有効化
Redis はデフォルトでは無効になっています。使用するには、config.toml で有効化する必要があります。
config.toml での設定:
["environments.dev.redis"]
enabled = true
sku_name = "Basic" # Basic, Standard, Premium
family = "C" # C (Basic/Standard) または P (Premium)
capacity = 0 # 0-6(容量)
SKU の選択: Basic (開発・テスト)、Standard (本番環境、冗長化あり)、Premium (高可用性・永続化が必要)
詳細は 公式ドキュメント を参照。
環境変数
Redis を有効化すると、以下の環境変数が自動的に設定されます(monolith、frontend-backend 両方のテンプレート)。
提供される環境変数: REDIS_URL(Redis 接続文字列)、REDIS_HOST, REDIS_PORT, REDIS_SSL_PORT, REDIS_PASSWORD
重要な注意事項:
- SSL は必須です: Azure Cache for Redis は SSL 接続が必須です(ポート 6380)
- REDIS_URL を推奨:
REDIS_URLを使用すると、接続コードが簡潔になります - データベース番号が含まれます:
REDIS_URLの形式はrediss://default:password@hostname:6380/0で、末尾の/0はデフォルトのデータベース番号です - Frontend と Backend 両方に注入: どちらのコンテナでも使用できます(frontend-backend テンプレートの場合)
詳細は Terraform が提供する環境変数仕様 を参照。
接続方法
Python での接続
1. redis-py を使用(基本)
インストール:
pip install redis
接続例(REDIS_URL を使用):
import os
import redis
# REDIS_URLから接続(推奨)
redis_url = os.getenv("REDIS_URL")
r = redis.from_url(redis_url, decode_responses=True)
# 接続テスト
try:
r.ping()
print("Connected to Redis")
except redis.ConnectionError as e:
print(f"Connection failed: {e}")
# 基本操作
r.set('key', 'value')
value = r.get('key')
print(value) # 'value'
2. コネクションプールの使用
本番環境では、コネクションプールの使用を推奨します:
import os
import redis
# REDIS_URLからコネクションプールを作成
redis_url = os.getenv("REDIS_URL")
pool = redis.ConnectionPool.from_url(
redis_url,
max_connections=10,
decode_responses=True
)
# Redis クライアントの作成
r = redis.Redis(connection_pool=pool)
# 使用例
r.set('user:1000:name', 'John Doe')
name = r.get('user:1000:name')
print(name) # 'John Doe'
3. FastAPI での使用
依存性注入パターン:
import os
import redis
from fastapi import FastAPI, Depends
app = FastAPI()
# Redis コネクションプール
redis_url = os.getenv("REDIS_URL")
redis_pool = redis.ConnectionPool.from_url(redis_url, decode_responses=True)
def get_redis():
"""Redis クライアントを取得"""
return redis.Redis(connection_pool=redis_pool)
@app.get("/cache/{key}")
def get_cache(key: str, r: redis.Redis = Depends(get_redis)):
"""キャッシュから値を取得"""
value = r.get(key)
if value is None:
return {"error": "Key not found"}
return {"key": key, "value": value}
@app.post("/cache/{key}")
def set_cache(key: str, value: str, ttl: int = 3600, r: redis.Redis = Depends(get_redis)):
"""キャッシュに値を設定"""
r.setex(key, ttl, value)
return {"key": key, "value": value, "ttl": ttl}
Node.js での接続
1. ioredis を使用(推奨)
インストール:
npm install ioredis
接続例(REDIS_URL を使用):
const Redis = require("ioredis");
// REDIS_URLから接続(推奨)
const redis = new Redis(process.env.REDIS_URL, {
retryStrategy(times) {
const delay = Math.min(times * 50, 2000);
return delay;
},
});
// 接続イベント
redis.on("connect", () => {
console.log("Connected to Redis");
});
redis.on("error", (err) => {
console.error("Redis error:", err);
});
// 基本操作
async function example() {
await redis.set("key", "value");
const value = await redis.get("key");
console.log(value); // 'value'
}
example();
2. Next.js API Routes での使用
Redis クライアントの作成 (lib/redis.js):
import Redis from "ioredis";
let redis;
export function getRedis() {
if (!redis) {
redis = new Redis(process.env.REDIS_URL, {
lazyConnect: true,
retryStrategy(times) {
const delay = Math.min(times * 50, 2000);
return delay;
},
});
}
return redis;
}
API ルート (pages/api/cache/[key].js):
import { getRedis } from "../../../lib/redis";
export default async function handler(req, res) {
const { key } = req.query;
const redis = getRedis();
if (req.method === "GET") {
// キャッシュから取得
const value = await redis.get(key);
if (!value) {
return res.status(404).json({ error: "Key not found" });
}
return res.status(200).json({ key, value });
}
if (req.method === "POST") {
// キャッシュに設定
const { value, ttl = 3600 } = req.body;
await redis.setex(key, ttl, value);
return res.status(200).json({ key, value, ttl });
}
res.status(405).json({ error: "Method not allowed" });
}
3. redis(node-redis)を使用
インストール:
npm install redis
接続例:
const redis = require("redis");
// Redis クライアントの作成
const client = redis.createClient({
socket: {
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT || "6380"),
tls: true,
},
password: process.env.REDIS_PASSWORD,
});
client.on("error", (err) => console.error("Redis Client Error", err));
// 接続
await client.connect();
// 基本操作
await client.set("key", "value");
const value = await client.get("key");
console.log(value); // 'value'
// 切断
await client.disconnect();
使用例
シナリオ 1: API レスポンスのキャッシュ(FastAPI)
import os
import redis
import json
from fastapi import FastAPI, Depends
app = FastAPI()
redis_pool = redis.ConnectionPool(
host=os.getenv("REDIS_HOST"),
port=int(os.getenv("REDIS_PORT", 6380)),
password=os.getenv("REDIS_PASSWORD"),
ssl=True,
decode_responses=True
)
def get_redis():
return redis.Redis(connection_pool=redis_pool)
def get_user_from_db(user_id: int):
"""データベースからユーザー情報を取得(時間がかかる)"""
# 実際のデータベースクエリ
return {"id": user_id, "name": "John Doe", "email": "john@example.com"}
@app.get("/users/{user_id}")
def get_user(user_id: int, r: redis.Redis = Depends(get_redis)):
"""ユーザー情報を取得(キャッシュあり)"""
cache_key = f"user:{user_id}"
# キャッシュから取得
cached = r.get(cache_key)
if cached:
print("Cache hit")
return json.loads(cached)
# キャッシュミス時はDBから取得
print("Cache miss")
user = get_user_from_db(user_id)
# キャッシュに保存(TTL: 1時間)
r.setex(cache_key, 3600, json.dumps(user))
return user
シナリオ 2: セッション管理(Next.js)
// lib/session.js
import { getRedis } from "./redis";
import { v4 as uuidv4 } from "uuid";
export async function createSession(userId, data) {
const redis = getRedis();
const sessionId = uuidv4();
const sessionKey = `session:${sessionId}`;
// セッションデータを保存(TTL: 24時間)
await redis.setex(sessionKey, 86400, JSON.stringify({ userId, ...data }));
return sessionId;
}
export async function getSession(sessionId) {
const redis = getRedis();
const sessionKey = `session:${sessionId}`;
const data = await redis.get(sessionKey);
return data ? JSON.parse(data) : null;
}
export async function deleteSession(sessionId) {
const redis = getRedis();
const sessionKey = `session:${sessionId}`;
await redis.del(sessionKey);
}
// pages/api/login.js
import { createSession } from "../../lib/session";
export default async function handler(req, res) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
const { username, password } = req.body;
// 認証処理(省略)
const user = authenticateUser(username, password);
if (!user) {
return res.status(401).json({ error: "Invalid credentials" });
}
// セッションを作成
const sessionId = await createSession(user.id, {
username: user.username,
email: user.email,
});
res.status(200).json({ sessionId });
}
シナリオ 3: レート制限(Python)
import os
import redis
from fastapi import FastAPI, HTTPException, Request
from datetime import datetime
app = FastAPI()
redis_pool = redis.ConnectionPool(
host=os.getenv("REDIS_HOST"),
port=int(os.getenv("REDIS_PORT", 6380)),
password=os.getenv("REDIS_PASSWORD"),
ssl=True
)
def get_redis():
return redis.Redis(connection_pool=redis_pool)
def check_rate_limit(client_ip: str, limit: int = 10, window: int = 60):
"""
レート制限をチェック
Args:
client_ip: クライアントIP
limit: 制限回数(デフォルト: 10回)
window: 時間ウィンドウ(秒)(デフォルト: 60秒)
Returns:
bool: 制限内ならTrue、超過したらFalse
"""
r = get_redis()
key = f"rate_limit:{client_ip}"
# 現在のカウントを取得
count = r.incr(key)
# 初回の場合、TTLを設定
if count == 1:
r.expire(key, window)
return count <= limit
@app.get("/api/data")
def get_data(request: Request):
"""レート制限付きAPI"""
client_ip = request.client.host
# レート制限をチェック
if not check_rate_limit(client_ip, limit=10, window=60):
raise HTTPException(
status_code=429,
detail="Rate limit exceeded. Try again later."
)
return {"data": "Some data"}
シナリオ 4: Pub/Sub(リアルタイム通知)
Publisher (Python):
import os
import redis
import json
r = redis.Redis(
host=os.getenv("REDIS_HOST"),
port=int(os.getenv("REDIS_PORT", 6380)),
password=os.getenv("REDIS_PASSWORD"),
ssl=True
)
# メッセージを発行
def publish_notification(user_id: int, message: str):
channel = f"notifications:{user_id}"
data = json.dumps({"message": message, "timestamp": datetime.now().isoformat()})
r.publish(channel, data)
print(f"Published to {channel}: {message}")
# 使用例
publish_notification(1000, "You have a new message")
Subscriber (Python):
import os
import redis
import json
r = redis.Redis(
host=os.getenv("REDIS_HOST"),
port=int(os.getenv("REDIS_PORT", 6380)),
password=os.getenv("REDIS_PASSWORD"),
ssl=True,
decode_responses=True
)
# Pub/Sub オブジェクトの作成
pubsub = r.pubsub()
# チャンネルを購読
pubsub.subscribe("notifications:1000")
# メッセージを受信
print("Waiting for messages...")
for message in pubsub.listen():
if message['type'] == 'message':
data = json.loads(message['data'])
print(f"Received: {data['message']}")
ベストプラクティス
1. コネクションプールを使用する
理由: Redis 接続の作成はコストが高いため、プールで再利用します。
Python:
pool = redis.ConnectionPool(
host=os.getenv("REDIS_HOST"),
port=int(os.getenv("REDIS_PORT", 6380)),
password=os.getenv("REDIS_PASSWORD"),
ssl=True,
max_connections=10
)
r = redis.Redis(connection_pool=pool)
Node.js (ioredis): デフォルトで自動的にコネクションプールを使用します。
2. TTL(有効期限)を設定する
理由: メモリ不足を防ぐため、すべてのキーに TTL を設定します。
# 1時間後に自動削除
r.setex('key', 3600, 'value')
# または
r.set('key', 'value')
r.expire('key', 3600)
3. 適切なキー命名規則を使用する
推奨パターン:
{object_type}:{id}:{attribute}
例:
r.set('user:1000:name', 'John Doe')
r.set('user:1000:email', 'john@example.com')
r.set('session:abc123:data', '{"user_id": 1000}')
r.set('cache:api:users:list', '[...]')
4. エラーハンドリングを実装する
import redis
from redis.exceptions import ConnectionError, TimeoutError
try:
r.set('key', 'value')
except ConnectionError:
print("Redis connection failed")
# フォールバック処理
except TimeoutError:
print("Redis timeout")
# リトライ処理
5. Pipeline を使用して複数コマンドを効率化
理由: ネットワークラウンドトリップを削減できます。
Python:
pipe = r.pipeline()
pipe.set('key1', 'value1')
pipe.set('key2', 'value2')
pipe.set('key3', 'value3')
pipe.execute()
Node.js (ioredis):
const pipeline = redis.pipeline();
pipeline.set("key1", "value1");
pipeline.set("key2", "value2");
pipeline.set("key3", "value3");
await pipeline.exec();
トラブルシューティング
接続できない
症状: ConnectionError や TimeoutError
原因と対処:
- 環境変数が正しく設定されているか確認
import os
print("REDIS_HOST:", os.getenv("REDIS_HOST"))
print("REDIS_PORT:", os.getenv("REDIS_PORT"))
# パスワードは表示しない
- Redis が有効化されているか確認
config.toml で redis.enabled = true になっているか確認します。
- SSL 接続を使用しているか確認
Azure Cache for Redis は SSL 必須です(ポート 6380)。
Python:
r = redis.Redis(
host=redis_host,
port=6380,
ssl=True # 必須
)
Node.js:
const redis = new Redis({
host: process.env.REDIS_HOST,
port: 6380,
tls: {}, // 必須
});
メモリ不足エラー
症状: OOM command not allowed when used memory > 'maxmemory'
原因: Redis のメモリが上限に達しています。
対処:
- TTL が設定されているか確認
# TTLが設定されていないキーを確認
for key in r.scan_iter():
ttl = r.ttl(key)
if ttl == -1: # TTL未設定
print(f"Key without TTL: {key}")
- 不要なキーを削除
r.delete('unnecessary_key')
- Redis の容量を増やす
config.toml で capacity を増やします。
パフォーマンスが遅い
原因と対処:
- 大きな値を保存している
Redis は小さな値の高速アクセスに最適化されています。大きな値(>1MB)は避けてください。
- Pipeline を使用していない
複数のコマンドを実行する場合は、Pipeline を使用してください。
- 不要な
KEYSコマンドを使用している
KEYS コマンドは本番環境では避け、SCAN を使用してください。
悪い例:
keys = r.keys('user:*') # 本番環境では非推奨
良い例:
for key in r.scan_iter('user:*'):
print(key)
まとめ
- Redis は config.toml で有効化が必要
- 環境変数は自動設定:
REDIS_HOST,REDIS_PORT,REDIS_PASSWORD - SSL 接続は必須: ポート 6380 を使用
- コネクションプールを使用: パフォーマンス向上
- TTL を設定: メモリ不足を防ぐ
Redis を活用して、高速でスケーラブルなアプリケーションを構築してください!
詳細は以下のドキュメントも参照してください:
- 環境変数ガイド - 環境変数の取得方法
- Terraform が提供する環境変数仕様 - 環境変数の詳細仕様