Redisのユースケース:キャッシュ、キュー、レート制限
Redisをスタックに追加したものの、単純なキーバリューストアとしてしか使っていませんか?Redisは強力なデータ構造とコマンドを提供し、キャッシュ、キューイング、レート制限をエレガントに解決できます。このガイドでは、これらのパターンを効果的に実装する方法を紹介します。
なぜこれらのユースケースにRedisなのか?
Redisはその速度と汎用性で知られるインメモリデータストアです。文字列、ハッシュ、リスト、セット、ソート済みセットなどをサポートしています。アトミック操作と組み込みの有効期限により、キャッシュ、キュー、レート制限に最適です。さらに、どの言語とも簡単に統合できます。
Redisによるキャッシュ
キャッシュは最も一般的なRedisのユースケースです。頻繁にアクセスされるデータをRedisに保存することで、データベースの負荷を軽減し、応答を高速化します。
基本的なキャッシュアサイドパターン
キャッシュアサイドパターンでは、アプリケーションはまずRedisをチェックします。データがなければ(キャッシュミス)、データベースから取得し、次回のためにRedisに保存します。
import redis
import json
r = redis.Redis(host='localhost', port=6379, db=0)
def get_user(user_id):
cache_key = f'user:{user_id}'
cached = r.get(cache_key)
if cached:
return json.loads(cached)
user = fetch_user_from_db(user_id) # your DB call
r.setex(cache_key, 3600, json.dumps(user)) # cache for 1 hour
return user
ベストプラクティス:
- 古いデータを避けるために、常に有効期限(TTL)を設定する。
user:123:profileのような意味のあるキー名を使用する。- キャッシュの無効化を処理する:データが変更されたら、キャッシュエントリを更新または削除する。
- メモリを節約するために、オブジェクトの複数のフィールドを保存するにはRedisハッシュの使用を検討する。
高度なキャッシュパターン
高トラフィックのアプリケーションでは、以下のパターンを検討してください:
- ライトスルー:キャッシュとデータベースを同時に更新する。
- ライトビハインド:まずキャッシュを更新し、その後非同期でデータベースを更新する。
- リフレッシュアヘッド:有効期限が切れる前にキャッシュエントリを積極的に更新する。
Redisによるキュー
Redisのリストとストリームは、シンプルなメッセージキューの構築に最適です。基本的なジョブキューにはリストを、より複雑なイベント処理にはストリームを使用します。
リストを使ったシンプルなジョブキュー
ジョブを追加するには LPUSH を、処理するには BRPOP を使用します。BRPOP はジョブが利用可能になるまでブロックするため、ワーカーにとって効率的です。
# Producer
r.lpush('jobs', json.dumps({'task': 'send_email', 'to': 'user@example.com'}))
# Consumer (worker)
while True:
_, job_data = r.brpop('jobs', timeout=30)
job = json.loads(job_data)
process_job(job)
ベストプラクティス:
- 異なるジョブタイプや優先度には別々のリストを使用する。
- 失敗したジョブ用にデッドレターキューを実装する。
BRPOPLPUSH(またはRedis 6.2+ではBLMOVE)を使用して、ジョブをアトミックに処理リストに移動し、信頼性を確保する。- ワーカーをスケールするために
LLENでキューの長さを監視する。
イベントソーシングのためのRedis Streams
Redis Streams(Redis 5.0で導入)は、コンシューマグループ、確認応答、リプレイを備えたより堅牢なキューを提供します。イベントを追加するには XADD を、消費するには XREADGROUP を使用します。
# Add event
r.xadd('events', {'type': 'order_placed', 'order_id': '123'})
# Consumer group
r.xgroup_create('events', 'order_processors', id='0', mkstream=True)
while True:
events = r.xreadgroup('order_processors', 'worker1', {'events': '>'}, count=10, block=5000)
for event in events:
process_event(event)
r.xack('events', 'order_processors', event.id)
Redisによるレート制限
レート制限はAPIを悪用から保護し、公平な使用を保証します。Redisのアトミック操作はリクエストのカウントに最適です。
固定ウィンドウレート制限
各ユーザーのカウンターをインクリメントし、有効期限を設定します。カウントが制限を超えた場合、リクエストをブロックします。
def is_allowed(user_id, limit=100, window=60):
key = f'rate_limit:{user_id}'
current = r.incr(key)
if current == 1:
r.expire(key, window)
return current