Redisのメモリリークとスパイクをトラブルシューティングするための4つの必須戦略
Redisは非常に高速なインメモリデータストアですが、そのパフォーマンスはメモリ管理に非常に敏感です。しばしば「リーク」と誤解される予期せぬメモリ増加や、突然のメモリスパイクは、高レイテンシ、不十分なエビクションパフォーマンス、ディスクへのスワッピング、そして最終的にはインスタンスの不安定化につながる可能性があります。
効果的なトラブルシューティングには、3つの異なる問題を見分ける必要があります。真のメモリリーク(稀で、通常はバグや不適切なライブラリ使用に関連)、際限のないデータ増加(最も一般的な問題で、多くの場合エビクションポリシーの欠如が原因)、そしてメモリの断片化/オーバーヘッド(システムレベルの非効率性)です。
このガイドでは、システム管理者と開発者が問題のあるRedisのメモリ使用パターンを特定し、デバッグし、安定させるのに役立つ、積極的な設定と反応的な診断ツールを組み合わせた4つの重要な戦略を概説します。
戦略1:使用状況と断片化メトリクスの詳細な監視
あらゆるメモリ問題を診断する最初のステップは、ベースラインを確立し、Redisがどのようにメモリ使用量を報告しているかを理解することです。標準のINFO memoryコマンドは、データによって使用されるメモリとオペレーティングシステムによって使用されるメモリを区別する重要なメトリクスを提供します。
診断のための主要メトリクス
スパイクが発生した場合、INFO memoryから以下の3つのメトリクスをすぐに確認してください。
used_memory: データおよび内部データ構造によって現在消費されているメモリ量(バイト単位)。これはRedisの内部アロケーターによって明示的に割り当てられたメモリです。used_memory_rss(Resident Set Size): オペレーティングシステムによってRedisプロセスに割り当てられた物理メモリ(RAM)の量。この数値には、used_memory、断片化、およびCopy-on-Writeのオーバーヘッドが含まれます。mem_fragmentation_ratio:used_memory_rss / used_memoryとして計算されます。これは断片化分析において最も重要なメトリクスです。
# 基本的なメモリ統計を確認
redis-cli INFO memory
# 出力例
# used_memory:1073741824 # 1 GBのデータ
# used_memory_rss:1509949440 # RAMで約1.5 GB
# mem_fragmentation_ratio:1.40625 # 40%の断片化
断片化率の解釈
- 比率が1.0に近い: 非常に良好。断片化が最小限。
- 比率が1.5を超える: 高い断片化。Redisが内部データ構造に必要な量よりも多くのメモリをOSに要求しており、RAMの無駄につながっています。
- 比率が1.0未満: 通常、メモリのスワッピングが発生していることを意味します。RedisデータがOSによってディスクに移動されています。これはパフォーマンスにとって壊滅的であり、インスタンスが過飽和状態であることを示します。
ヒント:
used_memory_rssの変動を注意深く監視してください。used_memoryが安定しているにもかかわらずused_memory_rssが急増している場合、問題は断片化、またはバックグラウンド永続化(AOFリライトやRDBスナップショット)によってトリガーされるCopy-on-Write(CoW)イベントに関連している可能性が高いです。
戦略2:堅牢なエビクションポリシーの実装
際限のない増加は、Redisにおけるメモリ「リーク」と認識されるものの最も頻繁な原因です。インスタンスがキャッシュとして使用される場合、maxmemoryディレクティブによって強制される、メモリ使用量の明確な上限が必須です。
maxmemoryが設定されていないか、0に設定されている場合、RedisはOSがプロセスを強制終了するまで利用可能なすべてのメモリを消費します。
maxmemoryの設定とポリシーの選択
redis.confで、またはCONFIG SETを使用して最大メモリ制限を指定します。
# 最大メモリを4GBに設定(利用可能なRAMの70-90%が推奨)
CONFIG SET maxmemory 4gb
# エビクションポリシーを設定
# allkeys-lru: *データセット全体*で最も最近使用されていないキーをエビクトする
CONFIG SET maxmemory-policy allkeys-lru
| ポリシー名 | 説明 | ユースケース |
|---|---|---|
noeviction |
デフォルト。メモリ制限に達すると書き込みコマンドでエラーを返す。 | データ損失が許容されないデータベース。 |
allkeys-lru |
有効期限に関係なく、最も最近使用されていないキーをエビクトする。 | 一般的なキャッシュ。 |
volatile-lru |
有効期限が設定されているキーのみの中から、最も最近使用されていないキーをエビクトする。 | 複合的なユースケース(永続化データ+キャッシュデータ)。 |
allkeys-random |
制限に達するとランダムなキーをエビクトする。 | シンプルなセッションストアやアクセスパターンが予測できない場合。 |
ベストプラクティス: 一般的なキャッシュワークロードの場合、
allkeys-lruはパフォーマンスと効率の最良のバランスを提供します。アプリケーション層のメモリフットプリントを正確に制御できない限り、デフォルトのnoevictionポリシーでキャッシュ層を実行しないでください。
戦略3:大規模キーによるスパイクの診断と整理
時には、メモリスパイクは何百万もの小さなキーによってではなく、ごく少数の極めて大規模なデータ構造によって引き起こされます。何百万もの要素を含む単一の管理の悪いHash、ZSET、またはListは、瞬時にギガバイトのRAMを消費する可能性があります。
redis-cli --bigkeysの使用
redis-cli --bigkeysユーティリティは、インスタンス内で最大のメモリ消費量を特定する最も簡単な方法です。データベースをスキャンし、要素数(必ずしもバイトサイズではないが、多くの場合相関がある)で最大のキーを報告します。
# bigkeys分析を実行
redis-cli --bigkeys
# 出力例(巨大なリストの特定)
---------- Summary ----------
...
[5] Biggest list found 'user:1001:feed' with 859387 items
MEMORY USAGEの使用 (Redis 4.0以降)
疑わしいキーの正確なサイズをバイト単位で確認するには、MEMORY USAGEコマンドを使用します。これは詳細な診断に不可欠です。
# 特定のキーのメモリ使用量を確認(バイト単位)
redis-cli MEMORY USAGE user:1001:feed
# 出力: (例) 84329014
大規模なキーを特定した場合は、それらのキーを生成しているクライアントコードを確認してください。大規模キーを軽減するための戦略には、次のものがあります。
- シャーディング: 大規模な構造(例:巨大なHash)を複数の小さなキーに分割します(例:
user:data:allの代わりに、user:data:segment1、user:data:segment2を使用します)。 - 有効期限: すべての大きな一時的なキーにTTL(Time to Live)を設定し、永続的な増加を防ぎます。
- クライアント監査: 大規模なキーは、際限のないクライアントループや、偶発的な大規模データセットの取り込みによって発生することがよくあります。
戦略4:メモリ断片化とCopy-on-Writeの管理
高いメモリ断片化(比率 > 1.5)や、Copy-on-Write(CoW)オーバーヘッドによる突然のRSSスパイクは、データリークと混同されがちな物理メモリの問題です。これらの問題は、メモリ割り当て器(通常はJemalloc)がメモリページを管理する方法と、永続化が動作する方法に関連しています。
アクティブデフラグメンテーション
Redis 4.0で導入されたアクティブデフラグメンテーションは、断片化が過度になった場合に、不要なメモリページを自動的に再利用するように機能します。これは、Redisを再起動せずにused_memory_rssを削減する最も速い方法であることがよくあります。
redis.confで有効にして設定します。
# アクティブデフラグメンテーションを有効にする
activedefrag yes
# デフラグを開始する前の最小断片化率(例:1.4)
active-defrag-threshold-lower 10
# デフラグが積極的に実行される前の最大断片化率(例:1.5)
active-defrag-threshold-upper 100
Copy-on-Writeオーバーヘッドの削減
RedisがRDBスナップショットまたはAOFリライトのために子プロセスをフォークすると、OSはCoW最適化を使用します。子プロセスがアクティブな間に親プロセスが大量の書き込みを実行すると、書き込まれたすべてのページが複製され、一時的にused_memory_rssが急増します。このスパイクにより、Redisのメモリフットプリントは簡単に2倍になる可能性があります。
軽減策:
- 永続化をスケジュールする: トラフィックの少ない期間に永続化を実行します。
- 十分な空きRAMを持つマシンでRedisを実行する: (例:
maxmemory設定の2倍)スワッピングなしでCoWスパイクを処理できるようにします。 - AOF永続化を使用する: 高いメモリ変動が重大な懸念である場合は、頻繁なRDBスナップショットの代わりにAOF永続化を使用します。AOFリライトはワークロードによっては、RDBスナップショットよりも負荷が少ない場合があります。
警告: Glusterのような積極的なメモリ割り当て器を使用するLinux上でRedisを実行している場合、または断片化以外の深刻なオーバーヘッドに気付いた場合は、Redisを開始する前に環境変数
MALLOC_ARENA_MAX=1を設定することを検討してください。これは割り当て器のメモリマッピング機能を制限し、特に制約のある環境でRSSを安定させるのに役立ちますが、同じマシン上の他のアプリケーションのマルチスレッドパフォーマンスにわずかな影響を与える可能性があります。
結論
Redisのメモリ問題のトラブルシューティングには、規律ある多層的なアプローチが求められます。真のリークは稀であり、メモリスパイクの大部分は、不適切なmaxmemory設定、予期せぬ大規模キー、または永続化イベントによって悪化した高い断片化によって引き起こされます。
正確な診断のためにINFO memoryを活用し、厳格なエビクションポリシーを強制し、定期的にサイズ超過キーを監査し、アクティブデフラグメンテーションを有効にすることで、Redisインスタンスを積極的に安定させ、高負荷下でも低レイテンシで信頼性の高いパフォーマンスを確保できます。