Dev Study
AWSサービスの内部原理 コース

99. 満杯のキャッシュは忘れることで生き延びる — maxmemoryポリシーのLRU/LFUとホットキーという偏り

キャッシュのメモリが上限に達したとき「何を忘れるか」がどう決まるのか、そしてハッシュで均しても残る2つ目の偏り=ホットキーがどこで生まれるのかを、図で追っていきます。

① 上限に達すると「忘れる」判断が始まる

書き込みが来る
到達したら maxmemory-policy を参照
上限未満そのまま保存
上限に到達空きが無い
ポリシーが2つの道を分ける
maxmemory-policyパラメータグループの設定値
天井での振る舞いは設計項目
エビクション実行既存のキーを1つ以上消して空きを作る
noeviction追い出さない=新規書き込みはエラーで拒否
  • 公式の言い回し: 「Eviction occurs when the max-memory on the cluster is consumed and a policy is in place to allow eviction.」——エビクションは「上限に達した」かつ「追い出しを許すポリシーがある」ときに起きる。
  • ポリシーは cluster parameter group の maxmemory-policy で管理する(公式: 「This policy can be managed using the maxmemory-policy on the cluster parameter group.」)。
  • noeviction は追い出しをしないポリシー。空きが作れないので、上限到達後は新しい値が保存されず書き込みがエラーになる(=容量を守る代わりに書き込みを犠牲にする)。Valkey公式の原文: 「New values aren't saved when memory limit is reached」。
  • DynamoDB編の「ストレージは無限に増えない、だからパーティションで分ける」と前提は同じ——メモリには天井があり、天井に当たったときの振る舞いを設計項目として選ぶ。

満杯のキャッシュには2つの道しかない——古いものを忘れて空きを作るか、書き込みを断るか。どちらを選ぶかは maxmemory-policy という1つのパラメータに集約されている。

② 「何を忘れるか」は対象×基準の2×2

対象: volatileTTL付きのキーだけを候補にする
対象: allkeys全キーを候補にする
組み合わせ+noevictionで計8値
基準: lru最近使われていない順
基準: lfu使用頻度が低い順
基準: randomランダムに選ぶ
基準: ttl期限が近い順(volatile専用)
volatile-lru★既定★ TTL付き×最近使わない順
TTLの無いキーは既定では保護される
他の6値allkeys-lru / allkeys-lfu / volatile-lfu / allkeys-random / volatile-random / volatile-ttl
noeviction追い出さない(①の右の道)
  • 既定は volatile-lru。公式の説明: 「The default policy volatile-lru frees up memory by evicting keys with a set expiration time (TTL value).」——TTL(期限)が設定されているキーの中から選ぶ。
  • 公式の言い回し: 「Least frequently used (LFU) and least recently used (LRU) policies remove keys based on usage.」——lru/lfu はどちらも「使われ方」で消す。
  • 8つの値は、AWS公式のパラメータグループドキュメントに maxmemory-policy の Permitted values として明記されている(Default: volatile-lru も同ページで確認)。なお lfu の2値は Redis OSS 4.0.10 で追加されたもの。
  • volatile-* はTTL付きの候補が尽きて空きを作れない場合、volatile-lru / volatile-lfu / volatile-random / volatile-ttl は noeviction と同様に振る舞い、書き込みが失敗しうる(Valkey公式: 「behave like noeviction if there are no keys to evict matching the prerequisites」)。allkeys-* は全キーが候補なのでこの詰まりが起きにくい。

エビクションの設計とは「対象(volatile/allkeys)×基準(lru/lfu/random/ttl)」の格子から1マスを選ぶこと。既定の volatile-lru は「期限付きのものから、最近使っていない順に」という保守的な初期値になっている。

③ Memcachedは自動LRU / 効きはヒット率

Memcachedノード既定のLRUが各ノードで自動的に働く
効き自体はもう1つの数字で測る
EvictionsCloudWatchメトリクス
追い出しの回数を監視
ヒット率 = hits ÷ (hits + misses)
ヒット (hits)キーがあった
ミス (misses)キーが無かった
ヒット率1に近いほどキャッシュが効いている
  • Memcached: 「there is a default LRU policy in place controlling evictions on each node.」——ノードごとにLRUが既定で働く(選ぶまでもなく自動)。
  • 「The number of evictions on your Amazon ElastiCache cluster can be monitored using the Evictions metric on Amazon CloudWatch.」
  • ヒット率(cache hits ratio)の公式定義: 「the total of key hits divided by the total hits and misses. The closer to 1 the ratio is, the more effective your cache is.」

エビクションはMemcachedでは自動、その回数は Evictions メトリクスで見える。キャッシュ全体の健全さは「ヒット率」という単一の比率に凝縮できる——可観測性編で p99 やアラームしきい値という1つの数字で系を測ったのと同じ発想。

④ ミスが多いなら4つに分類する

キャッシュミス要求したキーが無い
分類がそのまま対策の切り分けになる
evicted追い出された
deleted削除された
expired期限切れ
never existedそもそも入れていない
容量/ポリシー①②の設計を見直す
Evictionsが高ければまずここ
アプリのDEL等削除の経路を確認
TTL設計短すぎないか
キャッシュ戦略投入経路の問題
  • 公式の言い回し(ほぼ原文): 「A key is not in the cache because it either has been evicted or deleted, has expired, or has never existed.」——evicted / deleted / expired / never existed の4つ。
  • 4つのうち evicted だけが①②の容量・ポリシー設計に直結する。Evictions メトリクスが低いのにミスが多いなら、原因は残り3つ(削除・期限・未投入)側にある、と切り分けられる。

「ヒット率が低い」を漠然と眺めず、evicted/deleted/expired/never-existed の4象限に落とすと、直すべき層(容量設計なのか、TTLなのか、キャッシュ戦略なのか)が一意に決まる。

⑤ もう1つの偏り=ホットキー

key A普通のアクセス
key B普通のアクセス
key H超人気=ホットキー
アクセスが集中
Node1: key A
Node2: key B
Node3: key Hこのノードだけ不釣り合いに忙しい
CPU・ネットワークが1ノードに偏る
  • 公式: 「These data structures [lists, streams, sets, etc.] are stored in single Keys, which reside on a single node. A very large multi-dimensional key has the potential to utilize more network capacity and memory than other data types and can cause a disproportionate use of that node.」——list / stream / set のような多次元データ構造は「1キー=1ノード」に丸ごと載る。巨大キーは特に危険。
  • ハッシュは「配置」を均すが、どのキーが人気になるかは均せない——これが容量の偏り(エビクション)とは別の問題である理由。
  • DynamoDB編のホットパーティション(キーをハッシュで均等に分けても、特定のキーへのアクセスが集中すれば1つの区画だけが忙しくなる)の、完全な再演。

ハッシュは平均を均すが、人気は均せない。多次元データ構造は1キーがまるごと1ノードに載るため、巨大で人気なキーは単一ノードのCPUとネットワークを食い潰す。

⑥ 対策は検出・複製・往復削減の3手

ホットキー対策「見つける」「散らす」「そもそも聞きに行かない」
① 検出valkey-cli --hotkeys
LFU系max-memoryポリシーが前提
② 複製で分散ホットキーを複数ノードへ複製し読みを散らす
クライアントの責務(エンジンは代行しない)
③ 往復を減らすサーバー支援型クライアントサイドキャッシュ
Valkey 7.2以降 / Redis OSS 6以降
  • ① 「you can detect hot keys using valkey-cli --hotkeys if an LFU max-memory policy is in place.」——LFU系ポリシーが前提。
  • ② 「Consider replicating hot keys across multiple nodes ... This approach requires the client to write to multiple primary nodes (the ... node itself will not provide this functionality) and to maintain a list of key names to read from, in addition to the original key name.」——複数プライマリへの書き込みも、読み先キー名リストの管理もアプリの責務。メッセージング編・レプリカで見た「読みをレプリカに散らす」原理の、クライアント主導版。
  • ③ 「ElastiCache engine 7.2 for Valkey and above, and ElastiCache version 6 for Redis OSS and above, all support server-assisted client-side caching. This enables applications to wait for changes to a key before making network calls back to ElastiCache.」——キーの変更通知を待ち、変わるまでネットワーク往復をしない=ホットキーへの読みの多くをネットワークの外で吸収する。

ホットキーは「検出(--hotkeys)→複製で散らす(クライアントの責務)→往復を消す(サーバー支援キャッシュ)」の順に手当てできる。エビクションは容量の偏りへの答え、ホットキーはアクセスの偏りへの答え——どちらも「有限の速い記憶に何を残すか」というCSの古い問題が、マネージドキャッシュでは設定値とメトリクスの形で現れたものにすぎない。

公式ドキュメントで詳しく ↗