处理 TTL 与过期策略
目标
准确理解 TTL 的行为,重现并阻止 TTL 悄悄消失的陷阱,并通过实际的驱逐来确认驱逐策略之间的差异。
为什么重要
“Redis 内存一直在增长,却找不到原因”这类咨询,大部分都是没有设置 TTL 的键。而它之所以不显眼,是因为代码明明在好好地使用 SETEX,却在某处用 SET 只更新了值,把原本的生存时间抹掉了。本实验的第 3 步正是这一幕。还有一件要刻进脑子里的事:过期并不是立即发生的。Redis 要么在访问时删除,要么在后台抽取样本来删除。所以过期时间已过的键可能仍留在 DBSIZE 里,“明明过期了,为什么内存没有减少”的答案就在这里。最后的审计脚本可以原样带到生产环境中使用。
步骤
- 把
cache:a连同值一起以 60 秒的生存时间放入,并在/root/ttl/ttl1.txt中写入ttl=<남은초>(占位符为剩余秒数)。该值必须大于等于 1 且小于等于 60。 - 移除
cache:a的 TTL。/root/ttl/ttl2.txt中必须写有ttl=-1。 - 以 60 秒的生存时间放入
cache:b之后,用SET cache:b <새값>(占位符为新值)只更新值。把 TTL 变成 -1 的情况记录到/root/ttl/lost.txt,再把用KEEPTTL重试的结果,以ttl=<양수>(占位符为正数)的形式写入/root/ttl/kept.txt。 - 以 1 秒的生存时间放入
cache:c,等待 2 秒,然后把未访问状态下的DBSIZE与访问之后的DBSIZE,以before=<n> after=<n>的形式写入/root/ttl/lazy.txt。 - 把
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 的陷阱
以 60 秒的生存时间放入 cache:b 之后,用 SET cache:b <새값>(占位符为新值)只更新值。把 TTL 变成 -1 的情况记录到 /root/ttl/lost.txt,再把用 KEEPTTL 重试的结果,以 ttl=<양수>(占位符为正数)的形式写入 /root/ttl/kept.txt。
明明只更新了值,生存时间却消失了。有一个选项可以保持生存时间。
观察过期并不会立即归还内存
以 1 秒的生存时间放入 cache:c,等待 2 秒,然后把未访问状态下的 DBSIZE 与访问之后的 DBSIZE,以 before=<n> after=<n> 的形式写入 /root/ttl/lazy.txt。
过期时间已过、但没有人访问的键,会在一段时间内继续留着。对比一下查询前后的数量。
设置驱逐策略
把 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 的键。它必须是能在生产环境中定期运行的形式。