はじめに
社内のプロジェクトでAWSのElasticCache for Redisを利用する機会がありました。
その際、Redisメモリ利用量が100%に達する可能性があったので、そのあたりの挙動について調査・検証してみてみました。
調査
メモリが100%を超えようとする時どうなるのか
Redisはメモリが100%になったとしてもいきなり落ちることはありません。
Redisのメモリが100%になるかmaxmemory設定を超えた時、Eviction という事象が発生します。
Eviction とは直訳すると「立ち退き、明け渡し」的な意味で、既存のキーを削除しメモリを確保する挙動です。
Evictionの設定
Eviction発生によるキーの削除ルールはRedisのmaxmemory-policy
という設定で変更することができます。
(ElasticCacheのRedisだとパラメータグループの設定で確認・編集できます)
maxmemory-policy
の種類には以下のようなものがあります。キーに有効期限(TTL)があるかどうかなども重要になってきます。
maxmemory-policy | ルール |
---|---|
noeviction | メモリ制限に達したときに新しい値は保存されず、エラーを返す |
allkeys-lru | 一番最後にアクセスされたキーを削除してメモリを確保 |
allkeys-lfu | 最も使用頻度が少ないキーを削除してメモリを確保 |
volatile-lru | 有効期限のある、一番最後にアクセスされたキーを削除してメモリを確保 |
volatile-lfu | 有効期限のある、最も使用頻度が少ないキーを削除してメモリを確保 |
allkeys-random | ランダムにキーを削除してメモリを確保 |
volatile-random | 有効期限のあるキーをランダムに削除しメモリを確保 |
volatile-ttl | 有効期限が短いキーから削除してメモリを確保 |
ElastiCacheのRedisではvolatile-lru
がデフォルトの設定になっています。
LRUやLFUアルゴリズムについて、詳しく知りたい方はRedisのドキュメントを見てみてください。
検証
というわけで、次はDocker上でRedisを動かして検証してみます。
検証環境
・Windows 11
・Docker Desktop ver.4.6.1 Engine: 20.10.13
・Redis ver.6.2.6
Redisの準備手順
まず、Dockerを使ってRedis検証用のコンテナを作成します。
// redisのイメージをpull
docker pull redis
// redis検証用のコンテナを作成及び起動
docker run --name redis-inspection -d redis:latest
// redis検証用のコンテナに入る
docker exec -it redis-inspection bash
// redisに接続する
/data# redis-cli
127.0.0.1:6379>
次にRedisの設定を確認し、maxmemory-policy
とmaxmemory
を確認します。
/data# redis-cli
127.0.0.1:6379> INFO memory
.
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
.
DockerのRedisイメージのデフォルトだとmaxmemoryが0なので無限に保存できてしまいます。
また、noeviction
なのでメモリが溢れても自動削除されない設定になっています。
今回は検証なので、わざとメモリがあふれるように一時的にmaxmemoryを1MBくらいに設定しておきます。
(RedisのkeyのSET自体にもメモリが900KB前後ほど必要な模様)
127.0.0.1:6379> CONFIG SET maxmemory 1MB
OK
127.0.0.1:6379> INFO memory
maxmemory:1048576
maxmemory_human:1.00M
maxmemory_policy:noeviction
実験:メモリを溢れさせてみる
ここではnoeviction
とvolatile-lru
のパターンで検証してみます。
方法として、3000回ループして次から次へとkey valueのSETを試みます。
noeviction
の場合
:/data# for i in {1..3000} ; do echo ${i}; redis-cli SET ${i} test ; done
.
.
OK
2570
OK
2571
(error) OOM command not allowed when used memory > 'maxmemory'.
2572
(error) OOM command not allowed when used memory > 'maxmemory'.
.
.
途中からエラーが発生するようになりました。
(error) OOM command not allowed when used memory > 'maxmemory'.
2570回を超えた段階でメモリの上限に達し、2571回目からは書き込みエラーとなりました。
volatile-lru
の場合
まずはmaxmemory-policy
をvolatile-lru
に変更しておきます。
127.0.0.1:6379> CONFIG SET maxmemory-policy volatile-lru
OK
(有効期限付けないver)
:/data# for i in {1..3000} ; do echo ${i}; redis-cli SET ${i} test ; done
.
.
OK
2570
OK
2571
(error) OOM command not allowed when used memory > 'maxmemory'.
2572
(error) OOM command not allowed when used memory > 'maxmemory'.
.
.
volatile-lruは有効期限のあるキーが削除対象なので、こちらも同様のタイミングで書き込みエラーが発生しました。
(有効期限付きver)
次は全てのkeyに3600秒のTTLを設定して実行してみます。
:/data# for i in {1..3000} ; do echo ${i}; redis-cli SET ${i} test EX 3600 ; done
.
.
2999
OK
3000
OK
書き込みエラーが発生することなく、3000回keyのSETを実行することができました。
メモリが100%超えた後のSETでは、既存のキーを削除しメモリを確保している挙動が確認できました。
まとめ
・Redisはメモリが100%になったとしても、落ちることはない(Evictionが発生する)。
・max-memory-policyの設定によってEvictionの発生ルールを変えることで、100%になった際に上書きするかや受け付けないなどの設定することができる。
・ポリシーによっては、TTL(有効期限)の付け忘れによってキーが削除されないなど予期せぬエラーを生んでしまう可能性があるので注意しておく。