Redis 过期算法的影响是什么?

上次更新 2024 年 3 月 22 日

目标

许多缓存技术都共有过期键的能力。该机制很简单:任何在某个时间点需要过期的键都与生存时间 (TTL) 关联,并且数据库需要检测已过期的键并删除它们,采用可能的最低开销策略,这不是一个简单的问题。

本文档详细介绍了过期算法以及为了最小化内存消耗和 CPU 使用而进行的权衡取舍,从而最大化可用空间和性能。

解决方案

Redis 数据库中的键存储在哈希表(键空间)中,它们指向相关数据结构(集合、列表、哈希表等)。我们不会将 TTL 值保存在键中,因为在不需要过期的情况下(尤其在 Redis 未用作缓存时),这会导致内存占用过高。因此,临时键的 TTL 存储在一个辅助哈希表中,该哈希表通常小于主字典。该辅助哈希表存储指向键的指针,而该键指向 TTL 值。过期机制的工作方式如下

  • 被动过期。当访问某个键时,例如 GET 键,Redis 会检查该键是否存在且是否已过期。如已过期,将删除该键并返回 nil。但是,这还不够,因为可能存在不再被客户端访问的键。因此,第二个策略解决了这种情况。
  • 主动过期。这通过随机取样来实现。键不会按过期时间排序,找到合适的过期候选者的策略是抽取二级哈希表。

在 Redis 6 之前

原始方法(在 Redis 6 之前)仅仅是删除通过算法取样且具有过期 TTL 的那些键。该方法的问题在于,随着取样和删除键的循环进行,它找到具有过期 TTL 的键越来越少,这消耗资源且不会产生大量的驱逐,导致随机取样浪费 CPU 周期。正因如此,当适用于驱逐的好候选者低于可配置阈值时,算法会停止取样,默认设置为 25%。这意味着在最坏的情况下,25% 的键可能已被逻辑过期但仍然占用内存。

Redis 6 中的改进

在对备选方案进行原型制作和基准测试之后,已在 Redis 6 中添加一项改进功能以减少隐藏内存,也就是说,是先前实现不会释放的过期键分配的内存。该改进包括引入 基数树,作为二级哈希表的一种增补。在这次过期算法修订中

  1. 对二级哈希表进行过期键的取样仍在进行
  2. 将样本与基数树中已有的键进行比较
  3. 如果一个样本过期,则会立即删除;但如果一个样本即将过期(进一步阅读),则将其存储在基数树中。它是针对下一次取样迭代的提示,因此迭代从存储可能过期的键的基数树开始。

通过引入基数树,对样本的过期时间的了解会大写,并实现减少分配的隐藏内存。此解决方案有助于在不进行重大设计更改的情况下丢弃 25% 的隐藏键,并减少使用的内存量。


Redis 6.0 RC1 已于 2019 年 12 月 19 日星期四欧洲中部时间 09:58:24 发布

* The Redis active expire cycle was rewritten for much faster eviction of keys
  that are already expired. Now the effort is tunable.

建议

请参考 Redis 配置文件,以了解使用参数 active-expire-effort(可配置对系统中仍然存在的过期键的容限)对该算法进行配置的更多信息。一般规则是,在调整大小以使键设置有 TTL 的系统时,必须考虑隐藏内存并据此测试内存压力。

参考资料

  • Salvatore Sanfilippo 在 2019 年 RedisDay 上介绍了 Redis 键过期算法的演进。在此处观看演示。
  • 查看 expire.c 文件中的源代码,它详细介绍了键过期的算法。