Aman Kumar Singh

Redis 将所有数据保存在内存中,这自然会引出一个问题:是什么阻止它填满内存并崩溃?答案是两种机制协同工作。过期让键在设定时间后自动删除,这正是 Redis 能安全作为缓存的关键。淘汰则在达到内存上限时决定 Redis 丢弃哪些数据。理解这两者,才能让缓存安静地管理内存,而不会在最糟糕的时刻因内存不足而崩溃。

这是 Redis 大师班「基础模块」的第 8 部分,也是最后一部分,接在 有序集合 之后。

设置过期时间

你可以为键附加存活时间,Redis 会在时间到达后自动删除它:

SET session:abc "..." EX 3600     # expires in 3600 seconds
EXPIRE user:1:cache 300           # set a 5-minute TTL on an existing key
TTL user:1:cache                  # seconds remaining, -1 if none, -2 if gone
PERSIST user:1:cache              # remove the TTL, make it permanent

Enter fullscreen mode Exit fullscreen mode

过期机制让 Redis 成为安全的缓存。将数据库查询结果缓存并设置 5 分钟 TTL,它会自动刷新:5 分钟后键消失,下次请求未命中便会重新填充。你无需手动清理缓存数据,也不会让过期条目无限堆积。几乎所有缓存键都应设置 TTL,因为没有过期的缓存只是最终会耗尽的内存。

过期实际如何发生

值得注意的细节是:Redis 不会持续扫描过期键,而是采用两种机制。惰性过期在有请求访问已过期键时删除它(访问时检查 TTL,若已过期则移除)。主动过期由后台任务定期抽样删除已过期的键。

实际结果是,过期键在被清理前仍占用内存。通常这个过程很快,但大量键同时过期时可能短暂占用内存并引发删除操作峰值。通过在 TTL 中加入少量随机性分散过期时间,可以避免同时过期引发的「惊群」效应,也能防止稍后会讨论的缓存雪崩问题。

内存上限及达到上限时的行为

使用 maxmemory 可以限制 Redis 的内存用量。这在生产环境中必不可少:若不设置,Redis 会占用全部可用内存,最终被操作系统杀死。应留出足够空间给操作系统和 Redis 自身开销:

maxmemory 4gb
maxmemory-policy allkeys-lru

Enter fullscreen mode Exit fullscreen mode

maxmemory-policy 决定 Redis 在达到上限且新写入需要空间时执行的操作。这就是淘汰策略,正确选择它决定了系统是优雅降级还是报错。

淘汰策略

当 Redis 达到 maxmemory 时,策略决定移除哪些数据(或是否拒绝写入):

  • noeviction:内存满后拒绝写入并返回错误。读取仍正常。当 Redis 保存的数据不能丢失时(队列、数据源),宁可写入失败也不丢弃数据。
  • allkeys-lru:在所有键中淘汰最近最少使用的键。这是纯缓存的标准选择:保留热点数据,丢弃冷数据。
  • allkeys-lfu:淘汰使用频率最低的键。对于缓存而言通常优于 LRU,因为它保留持续热门的键,而非仅最近被访问一次的键。
  • volatile-lru / volatile-lfu / volatile-ttl:仅淘汰设置了 TTL 的键,按最近最少使用、最低使用频率或最快过期时间进行。适用于同一实例中混用带 TTL 的缓存数据和不带 TTL 的持久化数据。

策略选择取决于实例用途。专用缓存推荐 allkeys-lruallkeys-lfu。若实例同时存储缓存和重要数据,应使用 volatile-* 策略,仅淘汰带 TTL 的可丢弃键。若数据不能丢失,则选用 noeviction,并配合监控在填满前预警。

LRU 与 LFU 简述

LRU 淘汰最近未被访问的键;LFU 淘汰访问频率最低的键。LFU(Redis 4 引入)通常是更好的缓存策略,因为它保护长期热门的键,避免被一次性访问爆发而淘汰。若你在运行缓存且未明确选择,allkeys-lfu 是不错的默认选项。LRU 依然可用且更常见。

组合用于缓存

一个良好的 Redis 缓存需结合以上机制:所有缓存键均设置 TTL 以实现自动刷新和自清理;maxmemory 预留空间,防止 Redis 耗尽机器内存;采用 allkeys-lruallkeys-lfu 策略,在内存紧张时优雅淘汰冷数据。具备这三项后,Redis 可自行管理内存:热点数据保留,冷数据自然老化或被淘汰,实例长期稳定运行,无需人工清理或因内存不足崩溃。

过期与淘汰是让内存存储实用的安全机制。TTL 保持缓存数据新鲜并自动清理;maxmemory 和淘汰策略确保 Redis 在数据超出内存时仍受控。按实例用途(纯缓存或关键数据)有意识地配置这两者,Redis 即可在真实负载下保持健康。

至此,基础模块结束。下一篇将进入缓存模块,介绍最常见的 Redis 使用方式——缓存旁路模式。

要点总结

  • TTL(EXEXPIRE)使键自动删除,这是 Redis 能安全作为缓存的核心;几乎所有缓存键都应设置 TTL。
  • Redis 通过惰性(访问时)和主动(后台抽样)两种方式过期键,因此过期键在被回收前仍短暂占用内存。
  • 生产环境必须设置 maxmemory 并预留空间,否则操作系统最终会因 Redis 用尽内存而终止进程。
  • maxmemory-policy 决定淘汰方式:allkeys-lru/allkeys-lfu 适用于缓存,volatile-* 适用于混合实例,noeviction 适用于不可丢失的数据。
  • LFU 通常优于 LRU,因为它能保护持续热门的键,避免因单次访问爆发而被淘汰。

常见问题

如何让 Redis 键过期?

使用 SET key value EX seconds 或在已有键上执行 EXPIRE key seconds 设置 TTL。时间到达后 Redis 会自动删除。使用 TTL 查看剩余时间,PERSIST 可移除过期设置。

为什么缓存键需要 TTL?

这样缓存数据会自动刷新,过期条目不会无限堆积。TTL 到期后键消失,下次请求会重新填充。没有过期的缓存只是最终会填满的内存。

当 Redis 达到内存上限时会发生什么?

它会应用 maxmemory-policy:要么淘汰键(基于 LRU、LFU 或 TTL,作用于所有键或仅带 TTL 的键),要么在 noeviction 策略下拒绝新写入并返回错误。设置 maxmemory 让此过程可控,而非依赖操作系统终止。

应使用哪种淘汰策略?

纯缓存场景使用 allkeys-lruallkeys-lfu。若实例同时包含缓存和持久化数据,使用 volatile-* 策略,仅淘汰带 TTL 的键。若数据不可丢失,使用 noeviction 并配合监控。LFU 通常是缓存的最佳默认策略。

LRU 与 LFU 淘汰的区别?

LRU 淘汰最近最少访问的键;LFU 淘汰访问频率最低的键。LFU 通常更适合缓存,因为它保留持续热门的键,而非在其他键的一次性访问爆发后丢弃它们。

延伸阅读


本文最初发布于 amanksingh.com/blog/redis-key-expiration-ttl

关于作者

Aman Kumar Singh 是印度诺伊达的一名团队负责人兼高级软件工程师,专注于使用 TypeScript、Next.js、NestJS、PostgreSQL 和 Redis 进行全栈工程、系统设计和生产级 SaaS 写作。