Aman Kumar Singh

Redisはすべてのデータをメモリ上に保持するため、明らかな疑問が生じます。つまり、どのようにしてメモリが一杯になってクラッシュするのを防ぐのでしょうか?答えは、2つのメカニズムが連携して動作することです。有効期限によりキーは設定された時間後に自動的に削除され、これによりRedisはキャッシュとして安全に利用できます。削除ポリシーは、メモリの上限に達した場合にRedisが何を破棄するかを決定します。両方を理解することが、メモリを静かに管理するキャッシュと、最悪のタイミングでメモリ不足エラーでクラッシュするキャッシュの違いを生み出します。

これはRedisマスタークラスの基礎モジュールの第8部であり最終回で、ソート済みセットに続くものです。

有効期限の設定

キーに生存時間を設定すると、時間が経過するとRedisが自動的に削除します:

SET session:abc "..." EX 3600     # 3600 秒後に期限切れ
EXPIRE user:1:cache 300           # 既存のキーに5分間のTTLを設定
TTL user:1:cache                  # 残り秒数、 なしなら -1、 存在しないなら -2
PERSIST user:1:cache              # TTLを削除し、永続化する

Enter fullscreen mode Exit fullscreen mode

有効期限はRedisを安全なキャッシュにするものです。データベースクエリの結果を5分のTTLでキャッシュすると、自動的に更新されます。5分後にキーが消滅し、次のリクエストはキャッシュミスとなり再生成します。キャッシュデータを手動でクリーンアップする必要はなく、古いエントリが永遠に蓄積することもありません。ほぼすべてのキャッシュキーにはTTLを設定すべきです。なぜなら、有効期限のないキャッシュは最終的にメモリを使い果たすからです。

有効期限の実際の動作

有用な詳細:Redisは期限切れのキーを常時スキャンしません。2つのメカニズムを使用します。遅延期限切れは、誰かが期限切れのキーにアクセスしようとしたときに削除します(アクセス時にTTLを確認し、期限切れなら削除)。能動的期限切れは、バックグラウンドジョブが定期的にキーをサンプリングし、見つかった期限切れのキーを削除します。

実用的な結果として、期限切れのキーはどちらかのメカニズムが検知するまでメモリを占有し続けます。通常はほぼ瞬時に処理されますが、大量のキーが同時に期限切れになると、一時的にメモリを保持し、削除処理のスパイクを引き起こす可能性があります。有効期限を分散させる(TTLに少しランダム性を加える)ことで、同時に期限切れが集中する「サンダリング・ハード」を避け、後述するキャッシュ・スタンピード問題の防止にも役立ちます。

メモリの上限と上限到達時の動作

Redisのメモリはmaxmemoryで上限を設定します。これは本番環境では必須です。上限を設定しなければ、Redisは利用可能なRAMをすべて使い尽くし、オペレーティングシステムにkillされます。OSとRedis自身のオーバーヘッドのために余裕を持たせた値を設定します:

maxmemory 4gb
maxmemory-policy allkeys-lru

Enter fullscreen mode Exit fullscreen mode

maxmemory-policyは、上限に達し新しい書き込みに必要な空き領域が必要になったときにRedisが何をするかを決定します。これが削除ポリシーであり、正しく選択することで、正常な動作とエラーの違いが生まれます。

削除ポリシー

Redisがmaxmemoryに達すると、ポリシーが何を削除するか(または書き込みを失敗させるか)を決定します:

  • noeviction: 上限到達時に書き込みをエラーで拒否。読み取りは引き続き動作。Redisが失う余裕のないデータ(キューや信頼できる情報源)を保持している場合、データを削除するより書き込みを失敗させたいときに使用。
  • allkeys-lru: すべてのキーのうち、最も最近使用されていないキーを削除。純粋なキャッシュの標準的な選択肢:ホットデータを保持し、コールドデータを削除。
  • allkeys-lfu: 最も使用頻度の低いキーを削除。キャッシュではLRUより優れていることが多く、一度だけ最近アクセスされたキーよりも、一貫して人気のあるキーを保持。
  • volatile-lru / volatile-lfu / volatile-ttl: TTLが設定されたキーのみを、最近使用されていない、使用頻度が低い、または最も早く期限切れになる順に削除。TTL付きのキャッシュデータ(削除可能)と永続データ(TTLなし、保護対象)を同じインスタンスに混在させる場合に有用。

選択はインスタンスの使用方法に従います。専用キャッシュにはallkeys-lruまたはallkeys-lfuが適します。キャッシュと重要なデータを混在させるインスタンスには、TTL付きの破棄可能なキーのみを削除するvolatile-*ポリシーが適します。失う余裕のないデータを保持するインスタンスにはnoevictionと、満杯になる前に把握するための監視が必要です。

LRUとLFUの簡単な比較

LRUは最近触れられていないものを削除します。LFUは最も頻繁に触れられないものを削除します。Redis 4で追加されたLFUは、通常より優れたキャッシュポリシーです。時間とともに人気のあるキーを、一度のアクセスバーストで削除されることから保護するためです。キャッシュを実行していて意図的に選択していない場合、allkeys-lfuが良いデフォルトです。LRUも問題なく、より馴染みのある選択肢です。

キャッシュのための総合的な構成

適切に動作するRedisキャッシュは、これらすべてを組み合わせます:すべてのキャッシュキーにTTLを設定し、データの更新と古いエントリの自己削除を可能にします。maxmemoryはマシンを使い果たさないよう余裕を持たせて設定し、allkeys-lruまたはallkeys-lfuポリシーにより、メモリが逼迫したときにコールドデータを正常に削除します。これら3つが揃えば、Redisは自身のメモリを管理します:ホットデータは残り、コールドデータは古くなるか削除され、手動のクリーンアップやメモリ不足クラッシュなしにインスタンスは無期限に動作します。

有効期限と削除は、インメモリストアを実用的にする安全機構です。TTLによりキャッシュデータは最新に保たれ自己削除され、maxmemoryと削除ポリシーはデータがRAMを超えたときにRedisを範囲内に保ちます。インスタンスが純粋なキャッシュか失う余裕のないデータを保持するかに応じて、両方を意図的に設定すれば、Redisは実負荷の下でも健全に動作します。

これで基礎モジュールは完了です。次はキャッシュモジュールの本編として、アプリケーションがRedisを使用する最も一般的な方法であるキャッシュアサイドパターンを学びます。

要点

  • TTL(EXEXPIRE)によりキーは自動削除され、Redisを安全なキャッシュにします。ほぼすべてのキャッシュキーにはTTLが必要です。
  • Redisはキーを遅延(アクセス時)と能動的(バックグラウンドサンプリング)の両方で期限切れにし、期限切れキーは回収されるまで一時的にメモリを占有します。
  • 本番環境では常にmaxmemoryを余裕を持たせて設定します。設定しなければOSがRAMを使い果たしたRedisをkillします。
  • maxmemory-policyが削除を決定します:キャッシュにはallkeys-lru/allkeys-lfu、混在インスタンスにはvolatile-*、失う余裕のないデータにはnoeviction
  • LFUは通常、LRUより優れたキャッシュポリシーです。一貫して人気のあるキーを一度だけのアクセスバーストから保護するため。

よくある質問

Redisキーに有効期限を設定するには?

SET key value EX secondsでTTLを設定するか、既存のキーに対してEXPIRE key secondsを実行します。時間が経過するとRedisが自動的に削除します。残り時間はTTLで確認し、有効期限を削除するにはPERSISTを使用します。

キャッシュキーにTTLが必要な理由は?

キャッシュデータが自動的に更新され、古いエントリが永遠に蓄積しないようにするためです。TTL経過後にキーが消滅し、次のリクエストが再生成します。有効期限のないキャッシュは、最終的にメモリを埋め尽くすだけです。

Redisがメモリ上限に達するとどうなりますか?

maxmemory-policyを適用します:LRU、LFU、TTLベースで全キーまたはTTL付きキーのみを削除するか、noevictionの場合は新しい書き込みをエラーで拒否します。OSにkillされるのではなく制御されるようmaxmemoryを設定します。

どの削除ポリシーを使用すべきですか?

純粋なキャッシュにはallkeys-lruまたはallkeys-lfu。キャッシュと永続データを混在させるインスタンスには、TTL付きキーのみを削除するvolatile-*ポリシー。失う余裕のないデータにはnoevictionと監視。LFUは多くの場合、キャッシュの最良のデフォルトです。

LRUとLFUの削除の違いは?

LRUは最も最近アクセスされていないキーを削除します。LFUは最も頻繁にアクセスされていないキーを削除します。LFUは通常、キャッシュに適しています。一度だけのアクセスバーストで他のキーが削除されるのではなく、一貫して人気のあるキーを保持するためです。

参考文献


この記事は元々 amanksingh.com/blog/redis-key-expiration-ttl で公開されました。

著者について

Aman Kumar Singhはインドのノイダを拠点とするチームリーダー兼シニアソフトウェアエンジニアで、TypeScript、Next.js、NestJS、PostgreSQL、Redisを用いたフルスタックエンジニアリング、システム設計、本番SaaSについて執筆しています。