Redis 过期算法的影响有哪些?
最后更新日期:2024年3月22日
目标
键的过期功能是许多缓存技术的常见特性。其机制很简单:每个需要在某个时间点过期的键都关联有一个存活时间(TTL),数据库需要检测那些已过期的键并将其删除,同时采用成本最低的策略,但这并非一个简单的问题。
本文档详细介绍了过期算法以及权衡取舍,旨在最小化内存消耗和 CPU 使用率,从而最大化可用空间和性能。
解决方案
Redis 数据库中的键存储在一个哈希表(键空间)中,它们指向相关的数据结构(集合、列表、哈希等)。我们不在键中保存 TTL 值,因为在不需要过期的情况下(尤其是在 Redis 未用作缓存时),会产生内存开销。因此,易失性键的 TTL 存储在一个**辅助哈希表**中,该表通常比主字典小。这个辅助哈希表存储指向键的指针,而键则指向 TTL 值。过期机制的工作方式如下
- 被动过期。当一个键被访问时,例如 GET key,Redis 会检查该键是否存在以及是否已过期。如果已过期,该键会被删除并返回
nil
。然而,这还不够,因为可能存在客户端不再访问的键。所以第二种策略解决了这种情况。 - 主动过期。这通过随机抽样实现。键并非按过期时间排序,寻找适合过期候选键的策略是对辅助哈希表进行抽样。
Redis 6 之前
最初的方法(在 Redis 6 之前)是简单地删除算法抽样到的且 TTL 已过期的键。这种方法的问题在于,随着抽样和删除键的循环进行,它会触及越来越少 TTL 已过期的键,这会消耗大量资源并且不会产生显著的逐出数量,导致随机抽样浪费 CPU 周期。因此,当适合逐出的候选键数量低于可配置阈值(默认为 25%)时,算法就会停止抽样。这意味着在最坏的情况下,25% 的键可能在逻辑上已过期但仍占用内存。
Redis 6 中的改进
在对替代方案进行原型设计和基准测试后,Redis 6 中添加了一项改进,以**减少隐藏内存**的数量,即旧实现不会释放的已过期键占用的内存。这项改进包括在辅助哈希之外引入了基数树。在本次过期算法的修订中
- 仍然对辅助哈希表进行已过期键的抽样
- 将抽样结果与基数树中已有的键进行比较
- 如果抽样到的键已过期,它会立即被删除;但如果抽样到的键**即将**过期(请进一步阅读),它会被存储在基数树中。这是对下一次抽样迭代的提示,因此迭代会从存储可能即将过期的键的基数树开始。
通过引入基数树,抽样到的键的过期时间信息得到了利用,从而减少了分配的隐藏内存。这个解决方案在没有重大设计更改的情况下帮助减少了 25% 的隐藏键,同时也减少了使用的内存量。
Redis 6.0 RC1 发布,时间:2019年12月19日星期四 09:58:24 CEST
* 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 的键的系统进行容量规划时,必须考虑隐藏内存,并相应地测试内存压力。