ElastiCache Redisのメモリ溢れが起きたときの挙動、正しく理解できてますか??

2020.04.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは(U・ω・U) ElastiCacheおじさんを目指して日々鍛錬を続ける深澤です。

皆さん、ElastiCacheのRedisにどんどん情報を保存していって、メモリが溢れたらどんな挙動になると思いますか?ざっくりEvictionが発生してキーが削除されていくんじゃないの?と思われている方も多いかと思います。ですが実はそうとも限らないんですよ。今回はEvictionってなんだろう?というところから、メモリが溢れた場合の挙動について整理していきたいと思います。

Evictionとは??

どんどんデータが保存されて行ってmaxmemoryに到達した際、発生する挙動です。ちなみにCloudWatchのメトリクスで検知が可能です。

maxmemoryとはノードの中のメモリをどのくらいRedisに割り当てるかという値です。ElastiCacheの場合はパラメータグループで定義されています。

Evictionってどんなことするの?

Evctionの挙動ですが、以下のパターンから選択することが可能です。

  • noeviction
  • メモリ制限に達し、クライアントがより多くのメモリを使用する可能性のあるコマンドを実行しようとするとエラーを返します。
  • volatile-ttl
  • 有効期限(TTL)が短いキーから削除してスペースを確保します。
  • volatile-lfu
  • LRUアルゴリズムに基づき、有効期限(TTL)のあるキーの中で最もアクセスが少ないキーを失効させてスペースを確保します。
  • allkeys-lfu
  • LRUアルゴリズムに基づき、最もアクセスが少ないキーを失効させてスペースを確保します。
  • volatile-random
  • 有効期限(TTL)のあるキーをランダムに失効させてスペースを確保します。
  • allkeys-random
  • ランダムにキーを失効させてスペースを確保します。

LRUアルゴリズムですが、詳しく知りたい方はRedisの公式ドキュメントに書いてありますので参照ください。

デフォルトではvolatile-lruが設定されています。そうです、あくまで、有効期限(TTL)のあるキーの中でなんです。

TTLを設定しない状態でメモリが溢れるとどうなるの?

こんな感じのエラーが返ります。

OOM command not allowed when used memory

この場合、TTLも設定されていないのでいつまでもメモリは解放されませんし、Evictionによってもメモリ解放されないので手動でメモリ解放しないと永遠ElastiCacheが使えない状態に陥ります。

最大メモリまでメモリを使えるわけではない!?

先ほどmaxmemoryのお話をご案内しました。このmaxmemoryの値は、ElastiCacheのノードタイプで紹介されているメモリとほぼ同じです(数B、数KBくらいの誤差はあるかと)。

ここで紹介されているメモリは全てを使えるわけではありません。ElastiCacheのRedisには予約メモリと呼ばれるものがあります。

Redis2.8.22以降のバージョンの場合はmaxmemoryの25%がこの予約メモリとして確保されてます(それより前は50%)。なので例えばcache.r4.largeはメモリが12.3GBありますが、大凡10GBくらいまでしかデフォルトでは使えません。

予約メモリとは?

予約メモリとは、バックアップ(ElastiCacheでいうところのスナップショット)を取得する際に非同期でバックアップ用のプロセスを立ち上げるのですが、その時に使われるメモリです。パラメータグループを使用して値を調整できます(reserved-memory-percentというパラメータです)。0にすることも可能ですが、バックアップが正常に行われなかったりバックアップ中にレイテンシが発生してしまう可能性があることに注意してください。

最後に

今回はメモリ溢れの挙動とそれに伴う注意すべきパラメータをまとめました!今回ご紹介したパラメータはElastiCache Redis運用をしていく上では必須のパラメータですので是非覚えていただければと思います。いつの間にかElastiCacheに接続できなくなっていて提供しているプロダクトのレイテンシが増加したり、エラーが発生してしまうことは避けたいですが結構起きがちです。この辺りは監視にも気をつけたいですね。

以上、深澤(@shun_quartet)でした!