メインコンテンツまでスキップ

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

重要な注意事項:

  1. SSL は必須です: Azure Cache for Redis は SSL 接続が必須です(ポート 6380)
  2. REDIS_URL を推奨: REDIS_URL を使用すると、接続コードが簡潔になります
  3. データベース番号が含まれます: REDIS_URL の形式は rediss://default:password@hostname:6380/0 で、末尾の /0 はデフォルトのデータベース番号です
  4. 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();

トラブルシューティング

接続できない

症状: ConnectionErrorTimeoutError

原因と対処:

  1. 環境変数が正しく設定されているか確認
import os
print("REDIS_HOST:", os.getenv("REDIS_HOST"))
print("REDIS_PORT:", os.getenv("REDIS_PORT"))
# パスワードは表示しない
  1. Redis が有効化されているか確認

config.tomlredis.enabled = true になっているか確認します。

  1. 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 のメモリが上限に達しています。

対処:

  1. TTL が設定されているか確認
# TTLが設定されていないキーを確認
for key in r.scan_iter():
ttl = r.ttl(key)
if ttl == -1: # TTL未設定
print(f"Key without TTL: {key}")
  1. 不要なキーを削除
r.delete('unnecessary_key')
  1. Redis の容量を増やす

config.tomlcapacity を増やします。

パフォーマンスが遅い

原因と対処:

  1. 大きな値を保存している

Redis は小さな値の高速アクセスに最適化されています。大きな値(>1MB)は避けてください。

  1. Pipeline を使用していない

複数のコマンドを実行する場合は、Pipeline を使用してください。

  1. 不要な 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 を活用して、高速でスケーラブルなアプリケーションを構築してください!

詳細は以下のドキュメントも参照してください: