Redisのメモリ管理とEvictionポリシー:全8アルゴリズムの徹底解剖と、本番障害を防ぐ「正しい設計」の極意
テックリードの私だ。コードレビューや設計レビューで、こんなコードを見たことはないか?
> 「キャッシュだから、とりあえず `maxmemory` だけ設定して、ポリシーはデフォルトのまま放置しておけばいいか」
待て。それは本番障害への片道切符だ。
Redisはインメモリデータベースである以上、物理メモリの壁に必ず直面する。`maxmemory` に達した瞬間、Redisがどう振る舞うかは、あなたが選択した Evictionポリシー(逐次削除アルゴリズム) によって完全に決まる。ここで適切な選択を誤れば、キャッシュサーバーのつもりがアプリケーション全体のダウンを引き起こす凶器に変貌する。
今回は、Redisの `maxmemory-policy` で指定可能な全8つのアルゴリズムの内部挙動を丸裸にし、実務でどのポリシーをどう選択すべきか、私の知見をすべて叩き込む。
—
1. Evictionポリシーの全体像と選定の軸
まず前提として、RedisのEviction(逐次削除)は「書き込みコマンド(`SET`, `HSET`, `LPUSH` など)」が実行され、メモリ使用量が `maxmemory` の制限を超えたときにのみ発動する。読み取り専用のコマンドでは発動しない。
全8つのアルゴリズムは、以下の2つの軸でマッピングすると綺麗に整理できる。
1. 対象範囲: すべてのキー(`allkeys-`)か、有効期限(TTL)が設定されているキーのみ(`volatile-`)か
2. 削除基準: LRU(最近使われていない)か、LFU(頻繁に使われていない)か、Random(ランダム)か、TTL(寿命が短い)か、あるいは拒否(`noeviction`)か
これを踏まえて、一つひとつのアルゴリズムの急所を解説していこう。
—
2. 全8アルゴリズムの深掘り解説
① `noeviction` (デフォルト)
- 対象: すべてのキー
- 挙動: メモリ上限に達すると、書き込み系コマンドに対してエラー(`OOM command not allowed when used memory > ‘maxmemory’`)を返す。
- チーフアーキテクトの視点:
名前の通り「絶対にデータを消さない」ポリシー。一見するとデータロスを防げる安全な選択に見えるが、実務では最も危険な地雷になり得る。キャッシュとしての利用なら論外だし、永続化を伴うセッションストアであっても、メモリ枯渇と同時にアプリケーション全体の書き込みが完全停止するため、連鎖障害(カスケード障害)を引き起こす。原則、明確な意図がない限り本番環境では使用を避けるべきだ。
② `allkeys-lru` (Least Recently Used)
- 対象: すべてのキー
- 挙動: アクセスされてから最も時間が経過している(Least Recently Used)キーを削除する。
- チーフアーキテクトの視点:
王道のキャッシュ戦略。RedisのLRUは、厳密なLRUではなく近似LRUアルゴリズムを採用している。全キーから完璧なLRUを計算するのはCPUコストが高すぎるため、サンプリング(デフォルトでは5個のキーをランダムに抽出し、その中で最も古いものを削除)を行っている。`maxmemory-samples` パラメータでこの精度を調整可能だが、デフォルト(5〜10)で十分実用的な精度が出る。
③ `volatile-lru`
- 対象: 有効期限(TTL)が設定されているキーのみ
- 挙動: TTLを持つキーの中から、最もアクセスされていないものを削除する。
- チーフアーキテクトの視点:
「TTLが設定されていない=永続化すべき重要なマスターデータや設定値」と「TTL設定済みのキャッシュ」が同一のRedisインスタンスに同居している場合に用いる。しかし、そもそも永続データと揮発性キャッシュを同じRedisインスタンスに入れる設計自体がアンチパターンだ。インスタンスを分けるべきである。
④ `allkeys-random`
- 対象: すべてのキー
- 挙動: メモリ上限に達すると、ランダムにキーを削除してスペースを確保する。
- チーフアーキテクトの視点:
「おっ、ランダムならCPU負荷が一番低くて効率的じゃないか」と思ったそこのあなた。甘い。これを採用して良いのは、「すべてのキーのアクセス頻度や重要度が完全に均一である」という極めて特殊なケースだけだ。通常、ホットデータまで容赦なく消されるため、キャッシュヒット率が劇的に低下する。基本的には使わない。
⑤ `volatile-random`
- 対象: 有効期限(TTL)が設定されているキーのみ
- 挙動: TTLを持つキーの中からランダムに削除する。
- チーフアーキテクトの視点:
`allkeys-random` と同様の理由で、実務で積極的に選ぶ理由は見当たらない。「ランダムに消すぐらいなら、まだTTLが近いものを消したい」というのが人情だろう。次の `volatile-ttl` へ進む。
⑥ `volatile-ttl`
- 対象: 有効期限(TTL)が設定されているキーのみ
- 挙動: 残り生存時間(Time To Live)が最も短い(=消滅間近な)キーから順に削除する。
- チーフアーキテクトの視点:
一見理にかなっているように思える。寿命が短いものから消すのだから。だが、これも近似アルゴリズム(サンプリング)であり、本当にTTLが最短のものが選ばれるとは限らない。また、アクセス頻度を全く考慮しないため、「さっき猛烈にアクセスされたばかりだが、偶然TTLが短く設定されていたホットデータ」が容赦なく消される悲劇が起きる。
⑦ `allkeys-lfu` (Least Frequently Used) —— ★実務のゴールドスタンダード
- 対象: すべてのキー
- 挙動: アクセス頻度が最も低い(Least Frequently Used)キーを削除する。
- チーフアーキテクトの視点:
Redis 4.0で導入された革命児。LRUは「最後にアクセスされてからの時間」を見るため、「1時間前に爆発的に使われたが、それ以降全く使われていないバッチ処理用のデータ」などがメモリ居座り続ける弱点があった。
LFUは「アクセスの回数(頻度)」をカウントする。さらに秀逸なのは、時間経過とともにアクセス頻度カウンターが減衰(Decay)する仕組みを持っている点だ。これにより、「昔は人気だったが、今は飽和して使われないデータ」が自然淘汰される。現代の一般的なWebアプリケーションのキャッシュにおいては、これを指定しておけばまず間違いない。
⑧ `volatile-lfu`
- 対象: 有効期限(TTL)が設定されているキーのみ
- 挙動: TTLを持つキーの中で、アクセス頻度が最も低いものを削除する。
- チーフアーキテクトの視点:
前述の通り、永続データとキャッシュが混在しているレガシーな環境でのみ考慮する選択肢。新規設計でこれを選ぶ必要性はほぼない。
—
3. 実務でどう設計すべきか?(チーフアーキテクトからの提言)
設計レビューで私が見るポイントは以下の3点だ。
1. 原則は `allkeys-lfu` か `allkeys-lru`
キャッシュとしてRedisを使う場合、データが消えてもRDBなどのデータストアから再構築(キャッシュミス・フォールバック)できるはずだ。したがって、対象は「すべてのキー(`allkeys`)」とし、アルゴリズムはアクセスの傾向に応じて `allkeys-lfu`(迷ったらこれ)または `allkeys-lru` を選定せよ。
2. `maxmemory` のサイジングは「物理メモリの50%〜70%」が鉄則
OSのオーバーヘッド(特にLinuxのCopy-on-Writeによるfork時のメモリ肥大化)を忘れてはならない。RDBスナップショット(`BGSAVE`)やAOF rewriteを実行する際、Redisは親プロセスの最大2倍のメモリを消費する可能性がある。
`maxmemory` を物理メモリの限界値ギリギリに設定していると、Evictionが追いつく前にLinuxの OOM Killer にRedisプロセスそのものが強制終了させられる。本番障害の定番パターンだ。
3. 設定ファイル(`redis.conf`)のベストプラクティス設定例
最大メモリをシステムメモリ(例: 8GB)の 60% に設定
maxmemory 4831838208
現代のキャッシュ戦略の最適解:LFUによる全キー対象の逐次削除
maxmemory-policy allkeys-lfu
LFUの減衰率(デフォルトのままで大抵は十分だが、チューニング可能)
lfu-decay-time 1
サンプリング数(デフォルトの5。精度を上げたければ10程度に引き上げ)
maxmemory-samples 10
—
4. まとめ
Evictionポリシーの設定は、単なるパラメータチューニングではない。「システムがメモリ枯渇という極限状態に陥ったとき、どのデータを切り捨てて命をつなぐか」という、アーキテクチャの生死を分ける哲学の選択である。
デフォルトの `noeviction` をなんとなく放置しているエンジニアがいたら、今すぐこの記事を突きつけて設定を見直させなさい。プロのエンジニアであれば、システムのワークロード(アクセス頻度・データの性質)をロジカルに分析し、最適なポリシーをコードと設定でコード化できなければならない。
あなたのシステムのRedisは、極限状態を生き抜く準備ができているか? 今すぐ確認したまえ。
コメント