# Key eviction

Redis Key eviction策略概述(LRU、LFU 等)

当 Redis 用作缓存时,在添加新数据时让它自动驱逐旧数据通常很方便。这种行为在开发者社区中是众所周知的,因为它是流行的 memcached系统的默认行为。

本页涵盖了maxmemory用于将内存使用限制为固定数量的 Redis 指令的更一般主题。本页还深入介绍了 Redis 使用的 LRU 驱逐算法,这实际上是精确 LRU 的近似值。

# Maxmemory配置指令

maxmemory配置指令将 Redis 配置为为数据集使用指定数量的内存。您可以使用该文件设置配置指令redis.conf,或者稍后 CONFIG SET 在运行时使用该命令。

例如,要配置 100 兆字节的内存限制,您可以在redis.conf文件中使用以下指令:

maxmemory 100mb

设置maxmemory为零会导致没有内存限制。这是 64 位系统的默认行为,而 32 位系统使用 3GB 的隐式内存限制。

当达到指定的内存量时,逐出策略的配置方式决定了默认行为。Redis 可以为可能导致使用更多内存的命令返回错误,或者它可以逐出一些旧数据以在每次添加新数据时返回到指定的限制。

# 驱逐政策

当达到限制时,Redis 的确切行为maxmemory是使用maxmemory-policy配置指令配置的。

可以使用以下策略:

  • noeviction:达到内存限制时不保存新值。当数据库使用复制时,这适用于主数据库
  • allkeys-lru:保留最近使用的密钥;删除最近最少使用 (LRU) 键
  • allkeys-lfu : 保存常用键;删除最不常用 (LFU) 键
  • volatile-lru:删除最近最少使用的键,expire字段设置为true
  • volatile-lfuexpire :删除字段设置为的最不常用键true
  • allkeys-random:随机删除键为添加的新数据腾出空间。
  • volatile-random:随机删除expire字段设置为 的键true
  • volatile-ttl:删除expire字段设置为true和最短剩余生存时间(TTL)值的键。

如果没有匹配先决条件的驱逐键,则策略volatile-lruvolatile-lfuvolatile-randomvolatile-ttl 的行为类似于noeviction 。

根据应用程序的访问模式选择正确的驱逐策略很重要,但是您可以在应用程序运行时在运行时重新配置策略,并使用 Redis INFO 输出监控缓存未命中和命中的数量以调整您的设置。

一般来说,根据经验:

  • 如果您希望请求的受欢迎程度呈幂律分布,请使用**allkeys-lru策略。**也就是说,您期望元素的子集将比其他元素更频繁地访问。如果您不确定,这是一个不错的选择
  • 如果您有一个循环访问,其中所有键都被连续扫描,或者当您希望分布均匀时,请使用allkeys-random 。
  • 如果您希望能够通过在创建缓存对象时使用不同的 TTL 值向 Redis 提供关于哪些是好的过期候选者的提示,请使用volatile-ttl 。

volatile-lruvolatile-random策略主要在您想使用单个实例进行缓存并拥有一组持久键时非常有用。然而,运行两个 Redis 实例来解决这样的问题通常是一个更好的主意。

还值得注意的是,expire为键设置值会消耗内存,因此使用allkeys-lru 之类的策略会提高内存效率,因为不需要expire配置在内存压力下驱逐键。

# 驱逐过程如何运作

重要的是要了解驱逐过程的工作方式如下:

  • 客户端运行新命令,导致添加更多数据。
  • Redis 会检查内存使用情况,如果大于maxmemory限制,它会根据策略驱逐键。
  • 执行新命令,依此类推。

所以我们不断地越过内存限制的边界,越过它,然后通过驱逐键返回到限制之下。

如果某个命令导致在一段时间内使用大量内存(例如存储到新键中的大集合交集),则内存限制可能会被明显超过。

# 近似 LRU 算法

Redis LRU 算法不是一个精确的实现。这意味着 Redis 无法选择驱逐的最佳候选者,即过去访问最远的访问权限。相反,它将尝试运行 LRU 算法的近似值,通过对少量密钥进行采样,并驱逐采样密钥中最好的(具有最旧访问时间)的密钥。

然而,从 Redis 3.0 开始,该算法得到了改进,同时也采用了一个良好的候选池进行驱逐。这提高了算法的性能,使其能够更接近真实 LRU 算法的行为。

Redis LRU 算法的重要之处在于,您可以通过更改样本数量来调整算法的精度,以检查每次驱逐。此参数由以下配置指令控制:

maxmemory-samples 5

Redis 不使用真正的 LRU 实现的原因是因为它需要更多的内存。但是,对于使用 Redis 的应用程序,该近似值实际上是等效的。该图将 Redis 使用的 LRU 近似值与真正的 LRU 进行了比较。

LRU 比较

生成上述图表的测试用给定数量的键填充了 Redis 服务器。从第一个到最后一个访问密钥。第一个键是使用 LRU 算法驱逐的最佳候选者。后来增加了更多 50% 的密钥,以强制驱逐一半的旧密钥。

您可以在图中看到三种点,形成三个不同的带。

  • 浅灰色带是被驱逐的对象。
  • 灰色带是未被驱逐的对象。
  • 绿色带是添加的对象。

在理论上的 LRU 实现中,我们预计在旧密钥中,前半部分将过期。Redis LRU 算法只会以概率方式使旧密钥过期。

如您所见,与 Redis 2.8 相比,Redis 3.0 的 5 个样本做得更好,但大多数最新访问的对象仍由 Redis 2.8 保留。在 Redis 3.0 中使用 10 的样本大小,该近似值非常接近 Redis 3.0 的理论性能。

请注意,LRU 只是一个模型,用于预测给定密钥在未来被访问的可能性。此外,如果您的数据访问模式与幂律非常相似,那么大多数访问将在 LRU 近似算法可以很好地处理的密钥集中。

在模拟中,我们发现使用幂律访问模式,真正的 LRU 和 Redis 近似之间的差异很小或不存在。

但是,您可以以一些额外的 CPU 使用为代价将样本大小提高到 10,以接近真实的 LRU,并检查这是否会影响您的缓存未命中率。

使用该命令在生产中试验不同的样本量值CONFIG SET maxmemory-samples <count>非常简单。

# 新的 LFU 模式

从 Redis 4.0 开始, 最不常用的驱逐模式 可用。在某些情况下,此模式可能会更好地工作(提供更好的命中/未命中率)。在 LFU 模式下,Redis 会尝试跟踪项目的访问频率,因此很少使用的会被驱逐。这意味着所使用的密钥通常更有可能保留在内存中。

要配置 LFU 模式,可以使用以下策略:

  • volatile-lfu在具有过期集的键中使用近似的 LFU 驱逐。
  • allkeys-lfu使用近似的 LFU 驱逐任何密钥。

LFU 类似于 LRU:它使用概率计数器(称为 Morris 计数器 )来估计对象访问频率,每个对象仅使用几位,并结合衰减周期,以便计数器随着时间的推移而减少。在某些时候,我们不再希望将密钥视为经常访问的,即使它们是过去的,这样算法就可以适应访问模式的变化。

该信息的采样方式与 LRU (如本文档前面部分所述)选择驱逐候选人的情况类似。

然而,与 LRU 不同的是,LFU 具有某些可调参数:例如,如果不再访问排名较低的频繁项应该多快?还可以调整 Morris 计数器范围,以更好地使算法适应特定用例。

默认情况下,Redis 配置为:

  • 在大约 100 万个请求时使计数器饱和。
  • 每隔一分钟衰减一次计数器。

这些应该是合理的值并经过实验测试,但用户可能希望使用这些配置设置来选择最佳值。

有关如何调整这些参数的说明可以redis.conf在源代码分发的示例文件中找到。简而言之,它们是:

lfu-log-factor 10
lfu-decay-time 1

衰减时间是显而易见的,它是计数器应该衰减的分钟数,当采样并发现比该值更旧时。mean 的一个特殊值0:我们永远不会衰减计数器。

计数器对数因子改变了使频率计数器饱和所需的命中次数,恰好在 0-255 范围内。系数越高,达到最大值所需的访问次数就越多。系数越低,计数器对低访问的分辨率就越好,如下表所示:

+--------+------------+------------+------------+------------+------------+
| factor | 100 hits   | 1000 hits  | 100K hits  | 1M hits    | 10M hits   |
+--------+------------+------------+------------+------------+------------+
| 0      | 104        | 255        | 255        | 255        | 255        |
+--------+------------+------------+------------+------------+------------+
| 1      | 18         | 49         | 255        | 255        | 255        |
+--------+------------+------------+------------+------------+------------+
| 10     | 10         | 18         | 142        | 255        | 255        |
+--------+------------+------------+------------+------------+------------+
| 100    | 8          | 11         | 49         | 143        | 255        |
+--------+------------+------------+------------+------------+------------+

因此,基本上这个因素是在更好地区分低访问权限的项目与区分高访问权限的项目之间进行权衡。redis.conf示例文件中提供了更多信息。

# 反馈

如果您在此页面上发现问题,或有改进建议,请提交请求以合并或打开存储库中的问题。

Last Updated: 4/18/2023, 8:45:33 AM