【Redis】データは削除されたのですが、メモリ使用量がまだ多いのですが?
オペレーティング システムによって Redis に割り当てられたメモリは 6 GB ですが、インジケータ used_memory_human を使用すると、データの保存に 4 GB しか使用されていないことがわかります。これはなぜですか? データを保存できないのはなぜですか?
CONFIG SET maxmemory 100mb を通じて、または redis.conf 構成ファイルで maxmemory 100mb の Redis メモリ使用制限を設定します。最大メモリ サイズに達すると、メモリ削除ポリシーがトリガーされ、データが削除されます。
さらに、キーが有効期限に達すると、Redis は期限切れデータを削除するために次の 2 つの戦略を使用します。
- バックグラウンドでスケジュールされたタスクは、削除するデータの一部を選択します。
- 怠惰な削除。
Redis インスタンスが 5 GB のデータを保存し、2 GB のデータが削除されたと仮定すると、Redis プロセスが占有するメモリは減少しますか? (RSS とも呼ばれ、プロセスによって消費されるメモリ ページの数)。
答えは、Redis データが約 3 GB しか占有していない場合でも、約 5 GB のメモリを占有する可能性があるということです。
maxmemory を設定する必要があります。設定しないと、Redis は新しく書き込まれたデータにメモリを割り当て続けます。割り当てに失敗すると、アプリケーションはエラーを報告します。もちろん、それによってダウンタイムが発生することはありません。
解放された記憶はどこへ行くのでしょうか?
データは削除されましたが、top コマンドを使用してデータを表示すると、依然として大量のメモリを消費するのはなぜですか?
すべての記憶はどこへ行ったのでしょうか?infomemory コマンドを使用して、Redis メモリ関連のインジケーターを取得します。いくつかの重要なデータをリストしました。
127.0.0.1:6379> info memory
# Memory
used_memory:1132832 // Redis 存储数据占用的内存量
used_memory_human:1.08M // 人类可读形式返回内存总量
used_memory_rss:2977792 // 操作系统角度,进程占用的物理总内存
used_memory_rss_human:2.84M // used_memory_rss 可读性模式展示
used_memory_peak:1183808 // 内存使用的最大值,表示 used_memory 的峰值
used_memory_peak_human:1.13M // 以可读的格式返回 used_memory_peak的值
used_memory_lua:37888 // Lua 引擎所消耗的内存大小。
used_memory_lua_human:37.00K
maxmemory:2147483648 // 能使用的最大内存值,字节为单位。
maxmemory_human:2.00G // 可读形式
maxmemory_policy:noeviction // 内存淘汰策略
// used_memory_rss / used_memory 的比值,代表内存碎片率
mem_fragmentation_ratio:2.79
Redis プロセスのメモリ消費は主に次の部分で構成されます。
- 起動時に Redis 自体が占有するメモリ。
- ストレージオブジェクトデータメモリ。
- バッファ メモリ: 主に client-output-buffer-limit クライアント出力バッファ、コピー バックログ バッファ、および AOF バッファで構成されます。
- 記憶の断片化。
Redis 自体の空のプロセスが占有するメモリは非常に小さいため無視できますが、オブジェクト メモリが最も多く、すべてのデータがそこに格納されます。
バッファー内に大量のトラフィックがあるシナリオでは、制御不能になりやすく、Redis メモリーが不安定になる可能性があるため、特別な注意が必要です。
メモリの過度の断片化により、空き領域は確保できますが、データを保存できなくなります。
Fragmentation = used_memory_rss 実際に使用された物理メモリ (RSS 値) を used_memory で割った値 データが保存されている実際のメモリ。
記憶の断片化とは何ですか
メモリの断片化によりメモリ領域は空きますが、データは保存できません。たとえば、あなたと美しい女の子が映画館に映画を見に行ったら、あなたは間違いなく一緒にいたいと思うでしょう。
席が 8 つあり、チケットが 4 枚販売され、あと 4 枚空きがあるとします。しかし何と偶然だろうか、チケットを買った人たちはとても奇妙で、席を一つ空けてチケットを買っていた。
まだ4席ありますが、2席まとめて購入できないのでプレゼント!
メモリの断片化の原因
メモリの断片化の原因は何ですか?
主な理由は 2 つあります。
- メモリ アロケータの割り当て戦略。
- さまざまなサイズのキーと値のペアと削除操作: Redis は更新操作を頻繁に実行し、期限切れのデータを大量に削除します。解放された領域 (十分に連続していない) は再利用できず、断片化率が増加します。
次に、実際の理由について説明します...
メモリアロケータの割り当て戦略
Redis のデフォルトのメモリ アロケータは jemalloc を使用し、オプションのアロケータには glibc および tcmalloc が含まれます。
メモリ アロケータはオンデマンドで割り当てを行うのではなく、割り当てに固定範囲のメモリ ブロックを使用します。
例: 8 バイト、16 バイト...、2 KB、4KB 要求されたメモリが最近固定値に接続された場合、jemalloc はその固定値に最も近いスペースをメモリに割り当てます。
たとえば、プログラムが 1.5 KB しか必要としない場合、メモリ アロケータは 2 KB の領域を割り当て、この 0.5 KB が断片化されます。
これの目的は、メモリ割り当ての数を減らすことです。たとえば、データを保存するために 22 バイトのスペースを申請した場合、jemalloc は 32 バイトを割り当てます。後で 10 バイトを書き込む必要がある場合は、メモリ割り当てを申請する必要はありません。オペレーティング システムからのスペース。以前に要求された 32 バイトを使用できます。
キーが削除されても、Redis はすぐにメモリをオペレーティング システムに返しません。この状況は、基礎となるメモリ アロケータ管理が原因で発生します。たとえば、削除されたキーのほとんどは、他の有効なキーと同じメモリ ページにまだ割り当てられています。
さらに、空きメモリ ブロックを再利用するために、アロケータは元の 5 GB のデータのうち 2 GB を削除しますが、インスタンスにデータが再度追加されると、Redis の RSS は安定した状態を維持し、大きくなりすぎることはありません。
メモリ アロケータは基本的に、以前の削除によって解放された 2GB のメモリを再利用するためです。
キーと値のペアのサイズが異なる場合と削除操作
メモリ アロケータは固定サイズに従ってメモリを割り当てるため、通常は実際のデータが占有するよりも多くのメモリ領域を割り当てます。これにより、断片化が発生し、メモリ ストレージ効率が低下します。
さらに、キーと値のペアの頻繁な変更と削除により、メモリ領域の拡張と解放が行われます。たとえば、元々 32 バイトを占めていた文字列は、20 バイトを占める文字列に変更されるため、解放された 12 バイトはフリースペースです。
次のデータ ストレージ要求に 13 バイトの文字列が必要な場合、解放されたばかりの 12 バイトの領域は使用できず、断片化が発生します。
断片化の最大の問題は、スペースの総量は十分に大きいにもかかわらず、これらのメモリが連続的ではなく、データを保存できない可能性があることです。
メモリの断片化を解決する方法
では、どうすれば解決できるでしょうか?
まず、メモリの断片化が発生したかどうかを確認します。メモリの断片化率を示す、前の INFO メモリ コマンドによって要求された mem_fragmentation_ratio インジケーターに注目してください。
mem_fragmentation_ratio = used_memory_rss/ used_memory
1 < 断片化率 < 1.5 であれば妥当であると考えられますが、1.5 を超える場合は断片化率が 50% を超えていることを意味し、過剰な断片化率の問題を解決するために何らかの措置を講じる必要があります。
大法を再開する
最も単純かつ粗雑な方法は再起動することですが、永続化が有効になっていない場合、データは失われます。
永続化を有効にすると、RDB または AOF を使用してデータを復元する必要があり、インスタンスが 1 つしかなく、データが大きい場合、復旧フェーズで長時間サービスを提供できなくなり、高可用性が大幅に低下します。 。
メモリの断片を自動的にクリーンアップする
あなたが私のことをハンサムだと呼んでくれたので、私はあなたを手助けし、究極のトリックを教えます。Redis バージョン 4.0 以降では、メモリ断片化クリーニング メカニズムが提供されます。
掃除方法は?
非常に簡単ですが、引き続き上記の例を使用して、つながった 2 枚の映画チケットを購入したいとします。他者とのコミュニケーションによって立場を変えるだけで、それは達成されます。
Redis の場合、連続したメモリ空間がいくつかの不連続な空間に分割されると、オペレーティング システムはまずデータを移動して結合し、元のデータが占有していた空間を解放して連続的な空きメモリ空間を形成します。。
以下に示すように:
メモリの断片化を自動的にクリーンアップするコスト
自動クリーニングは良いことですが、任意に実行しないでください。オペレーティング システムはデータを新しい場所に移動し、元の領域を解放するためにリソースを消費します。
Redis のデータ操作命令はシングルスレッドであるため、データのコピーや移動を行う場合、断片化が解消された後でないとリクエストを処理できないため、パフォーマンスが低下します。
フラグメントのクリーンアップによるパフォーマンスへの影響を回避し、自動クリーニングを実現するにはどうすればよいですか?
良い質問ですね。次の 2 つのパラメーターを使用して、メモリ断片化のクリーンアップと終了のタイミングを制御し、CPU の過剰な占有を回避し、Redis 処理リクエストにおける断片化のクリアによるパフォーマンスへの影響を軽減します。
メモリ断片化の自動クリーニングを有効にする
CONFIG SET activedefrag yes
これは自動クリーニングをオンにするだけであり、クリーニング動作を開始するには、次の 2 つの条件を同時に満たす必要があります。
洗浄条件
active-defrag-ignore-bytes 200mb: メモリの断片化によって占有されているメモリが 200MB に達し、クリーンアップが開始されます。
active-defrag-threshold- lower 20: メモリ断片化の領域割合が、システムによって Redis に割り当てられた領域の 20% を超え、クリーンアップが開始されます。
パフォーマンスへの影響を回避する
クリーンアップ時間が利用可能になったので、クリーンアップがパフォーマンスに与える影響を制御する必要があります。1 つまたは 2 つの設定を使用して、まずフラグメントのクリアによって占有される CPU リソースを割り当て、Redis 処理リクエストのパフォーマンスへの影響を回避しながらフラグメントを正常にクリアできるようにします。
active-defrag-cycle-min 20: 自動クリーニング プロセス中に占有される CPU 時間の割合は 20% 以上であるため、クリーニング タスクが正常に実行されることが保証されます。
active-defrag-cycle-max 50: 自動クリーニング プロセスが占める CPU 時間の割合は 50% を超えることはできません。50% を超えると、Redis のブロックや大幅な遅延の発生を避けるためにクリーニングが直ちに停止されます。
要約する
データを保存するために Redis が占有するメモリが、オペレーティング システムによって Redis に割り当てられたメモリよりもはるかに小さいにもかかわらず、データを保存できない場合は、大量のメモリの断片化が発生している可能性があります。
infomemory コマンドを使用して、メモリ断片化 mem_fragmentation_ratio インジケーターが正常かどうかを確認します。
次に、自動クリーニングを有効にし、クリーニングのタイミングと CPU リソースの使用量を合理的に設定します。このメカニズムにはメモリのコピーが含まれるため、Redis のパフォーマンスに潜在的なリスクが生じます。
Redis のパフォーマンスが低下している場合は、フラグメントのクリーンアップが原因であるかどうかを確認し、その原因である場合は、active-defrag-cycle-max の値を減らします。