TTLと期限切れポリシーを扱う
目標
TTLの動作を正確に理解し、TTLが静かに消えてしまう落とし穴を再現して防ぎ、エビクションポリシーの違いを、実際のエビクションで確認します。
なぜ重要なのか
「Redisのメモリが増え続けるが、原因がわからない」という問い合わせの大半は、TTLを付けていないキーです。そしてそれが目立たない理由は、コードがSETEXをきちんと使っているのに、どこかでSETで値だけを更新して、もともとあった寿命を消してしまうからです。このラボのステップ3が、まさにその場面です。もう1つ体に残すべきことは、期限切れが即時ではないという事実です。Redisは、アクセスしたときに消すか、バックグラウンドでサンプルを取って消します。そのため、期限切れの時刻が過ぎたキーがDBSIZEに残っていることがあり、「期限切れになったのに、なぜメモリが減らないのか」の答えがここにあります。最後の監査スクリプトは、運用にそのまま持っていって使えるものです。
ステップ
cache:aを値と一緒に60秒の寿命で入れ、/root/ttl/ttl1.txtにttl=<남은초>を書きます(プレースホルダーは残り秒数です)。値は1以上60以下です。cache:aのTTLを削除します。/root/ttl/ttl2.txtにttl=-1と書かれている必要があります。cache:bを60秒の寿命で入れたあと、SET cache:b <새값>で値だけを更新します(プレースホルダーは新しい値です)。TTLが-1になることを/root/ttl/lost.txtに記録し、KEEPTTLでもう一度試した結果を/root/ttl/kept.txtにttl=<양수>で書きます(プレースホルダーは正の数です)。cache:cを1秒の寿命で入れて2秒待ったあと、アクセスしていない状態のDBSIZEとアクセス後のDBSIZEを、/root/ttl/lazy.txtにbefore=<n> after=<n>で書きます。maxmemory-policyをallkeys-lfuに設定します。/root/ttl/policy.txtにmaxmemory-policy allkeys-lfuが入っている必要があります。maxmemoryを8mbに下げ、/root/ttl/fill.pyでキーを十分に入れて、エビクションを起こします。/root/ttl/evicted.txtにevicted_keys=<n>を書き、nは0より大きい必要があります。/root/ttl/audit.shでSCANで走査しながら、TTLが-1のキーを/root/ttl/nottl.txtに書きます。スクリプトにKEYSという文字列があってはならず、結果ファイルに最低1個のキーが必要です。
参考
- 寿命の取得:
TTL key。-1は期限なし、-2はキーなしです。 - TTLを維持した更新:
SET key val KEEPTTL - エビクションの統計:
redis-cli INFO stats | grep evicted_keys - TTLのジッター:
ttl = base + random(-spread, +spread)で、同時の期限切れを散らします。 - よくある間違いは、
volatile-lruポリシーでキャッシュキーにTTLを付け忘れることです。そのキーは永遠にエビクトされません。
TTLをかけて残りの寿命を確認する
cache:aを値と一緒に60秒の寿命で入れ、/root/ttl/ttl1.txtにttl=<남은초>を書いてください(プレースホルダーは残り秒数です)。値は1以上60以下です。
寿命を一緒に指定して入れるコマンドもあり、すでにあるキーにかけるコマンドもあります。残りの寿命を取得してみてください。
TTLを削除して永続キーにする
cache:aのTTLを削除してください。/root/ttl/ttl2.txtにttl=-1と書かれている必要があります。
TTLのないキーの取得結果は-1です。-2は別の意味なので注意してください。
SETがTTLを消してしまう落とし穴を再現して防ぐ
cache:bを60秒の寿命で入れたあと、SET cache:b <새값>で値だけを更新してください(プレースホルダーは新しい値です)。TTLが-1になることを/root/ttl/lost.txtに記録し、KEEPTTLでもう一度試した結果を/root/ttl/kept.txtにttl=<양수>で書きます(プレースホルダーは正の数です)。
値だけを更新したのに、寿命が消えます。寿命を維持するオプションがあります。
期限切れが即座のメモリ返却ではないことを観察する
cache:cを1秒の寿命で入れて2秒待ったあと、アクセスしていない状態のDBSIZEとアクセス後のDBSIZEを、/root/ttl/lazy.txtにbefore=<n> after=<n>で書いてください。
期限切れの時刻が過ぎたのに誰もアクセスしていないキーは、しばらく残っています。取得の前後で個数を比べてみてください。
エビクションポリシーを設定する
maxmemory-policyをallkeys-lfuに設定してください。/root/ttl/policy.txtにmaxmemory-policy allkeys-lfuが入っている必要があります。
デフォルト値はキャッシュ用途に合いません。キャッシュ専用ならどのポリシーがよいか、読み物を参考にしてください。
実際にエビクションを起こす
maxmemoryを8mbに下げ、/root/ttl/fill.pyでキーを十分に入れて、エビクションを起こしてください。/root/ttl/evicted.txtにevicted_keys=<n>を書き、nは0より大きい必要があります。
メモリの上限を小さくして、キーをたくさん入れればよいです。エビクションの統計はINFOにあります。
TTLのないキーの監査スクリプトを作る
/root/ttl/audit.shでSCANで走査しながら、TTLが-1のキーを/root/ttl/nottl.txtに書いてください。スクリプトにKEYSという文字列があってはならず、結果ファイルに最低1個のキーが必要です。
SCANで走査しながら、残りの寿命が-1のキーを集めます。運用で定期的に実行できる形である必要があります。