Valkey/Redisで実装するRate Limiting戦略:アルゴリズム別の比較と実装例
概要¶
Rate Limiting のアルゴリズム紹介と Valkey/Redis での実装例。「Rate Limit は難しい問題」という認識のとおり、方式ごとにトレードオフがある。
詳細¶
主なアルゴリズム¶
1. Fixed Window Counter(固定ウィンドウ)¶
時間枠: 0:00〜0:59、1:00〜1:59 などの固定区間ごとにカウント
実装(Redis):
INCR rate:user123:202605031200 # 分単位のキー
EXPIRE rate:user123:202605031200 60
問題: ウィンドウの境界で2倍のリクエストが通る(バースト問題)
→ 0:59 に 100回、1:00 に 100回 = 1秒間で 200回通過してしまう
2. Sliding Window Log(スライディングウィンドウログ)¶
実装(Redis Sorted Set):
ZADD rate:user123 {timestamp} {request_id}
ZREMRANGEBYSCORE rate:user123 0 {now - window_size}
ZCARD rate:user123 # 現在の件数
利点: 精確なレートリミット、バースト問題なし
欠点: 全リクエストのタイムスタンプを保存 → メモリ消費大
3. Sliding Window Counter(スライディングウィンドウカウンター)¶
前の区間のカウントを重み付けして近似計算する方法:
current_rate = prev_count * (1 - elapsed/window) + curr_count
利点: 固定ウィンドウより精確、ログより省メモリ
欠点: 近似値(厳密ではない)
4. Token Bucket(トークンバケット)¶
バケツにトークンが一定速度で補充される。
リクエストのたびにトークンを消費。トークンがなければ拒否。
実装(Lua スクリプトでアトミックに):
local tokens = redis.call('GET', key) or capacity
if tokens > 0 then
redis.call('SET', key, tokens - 1)
redis.call('EXPIRE', key, ttl)
return 1 -- 許可
else
return 0 -- 拒否
end
利点: バーストを一定程度許容する(貯まったトークンを一気に使える)
用途: API の利用量制限(GitHub API など)
5. Leaky Bucket(リーキーバケット)¶
一定速度でリクエストを処理する(超過分はキューかドロップ)。
Token Bucket と逆のイメージ: 出力レートを一定に保つ。
利点: 一定のレートでバックエンドに負荷をかけられる
用途: 送信レートの平滑化(スパイクを均す)
Valkey/Redis での実装選択¶
シンプルに使う → Fixed Window(INCR + EXPIRE)
精確に制御したい → Sliding Window Log(Sorted Set)
省メモリで精確に → Sliding Window Counter
バースト許容 → Token Bucket(Lua スクリプト)
レート均一化 → Leaky Bucket
なぜ重要か / いつ使うか¶
- API の Rate Limiting を実装するとき
- DDoS 対策や不正利用の制限を設計するとき
- 面接で「Rate Limiting の実装」を聞かれたとき