将Redis用做LRU缓存

将Redis用做LRU缓存

当Redis用做缓存时,一般很方便在添加新数据时让它自动逐出旧数据。此行为在开发人员社区中是众所周知的,由于它是流行的内存缓存系统的默认行为 。redis

LRU实际上只是支持的驱逐方法之一。本页涵盖Redis maxmemory指令的更通常主题,该指令用于将内存使用量限制为固定数量,而且还深刻介绍了Redis使用的LRU算法,其实是确切的LRU的近似值。算法

从Redis版本4.0开始,引入了新的LFU(最不经常使用)驱逐策略。本文档的单独部分对此进行了介绍。缓存

Maxmemory配置指令
使用maxmemory配置指令是为了将Redis配置为对数据集使用指定的内存量。可使用redis.conf文件来设置配置指令,或者稍后在运行时使用CONFIG SET命令来设置。服务器

例如,为了配置100 MB的内存限制,能够在redis.conf文件内部使用如下指令。dom

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

当达到指定的内存量时,能够在不一样的行为之间进行选择,这称为策略。Redis只会为可能致使使用更多内存的命令返回错误,或者它能够逐出某些旧数据,以便在每次添加新数据时返回到指定的限制。测试

驱逐政策
maxmemory使用maxmemory-policy配置指令配置达到限制时,会发生确切的行为Redis 。对象

可使用如下策略:内存

noeviction:在达到内存限制而且客户端尝试执行可能致使使用更多内存的命令时返回错误(大多数写入命令,但DEL和一些其余异常)。
allkeys-lru:经过尝试先删除较新使用的(LRU)键来退出键,以便为添加的新数据腾出空间。
volatile-lru:经过尝试先删除较新使用的(LRU)密钥来退出密钥,但仅在已设置了expire set的密钥之间,以便为添加的新数据腾出空间。
allkeys-random:随机逐出密钥,以便为添加的新数据腾出空间。
volatile-random:随机逐出键,以便为添加的新数据腾出空间,但仅逐出设置了expire set的键。
volatile-ttl:逐出设置了expire的键,并尝试首先逐出具备较短生存时间(TTL)的键,以便为添加的新数据腾出空间。
该政策挥发性-LRU,挥发性随机和挥发性-TTL的行为很像noeviction若是没有钥匙驱逐匹配的先决条件。开发

选择正确的逐出策略很重要,具体取决于应用程序的访问模式,可是您能够在应用程序运行时在运行时从新配置该策略,并使用Redis INFO输出监视缓存未命中和命中的次数,以调整设置。 。

通常而言,根据经验:

当您但愿请求的流行程度具备幂律分布时,请使用allkeys-lru策略,也就是说,您但愿访问元素的子集比访问其余元素更多。若是不肯定,这是一个不错的选择。
若是您具备对全部密钥进行连续扫描的周期性访问,或者当您指望分布是统一的(全部元素以相同的几率被访问)时,请使用allkeys-random。
若是您但愿可以在建立缓存对象时经过使用不一样的TTL值向Redis提供有关哪些是最佳到期候选者的提示,请使用volatile-ttl。
当您要使用单个实例进行缓存并拥有一组持久密钥时,volatile-lru和volatile-random策略主要有用。可是,一般最好运行两个Redis实例来解决此问题。

还值得注意的是,将密钥设置为过时会消耗内存,所以使用诸如allkeys-lru之类的策略会提升内存效率,由于无需为要在内存压力下驱逐密钥设置过时。

驱逐过程如何进行
重要的是要了解驱逐过程的工做方式以下:

客户端运行新命令,从而添加更多数据。
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会尝试跟踪物品的访问频率,所以,极少使用的物品会被驱逐,而常用的物品则有更高的机会保留在内存中。

若是您认为在LRU,最近访问过但实际上几乎从未请求过的项目不会过时,所以风险在于逐出未来有更高机会被请求的密钥。LFU没有这个问题,一般应该更好地适应不一样的访问模式。

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

volatile-lfu 使用具备过时集的密钥在近似LFU中进行驱逐。
allkeys-lfu 使用近似的LFU退出任何密​​钥。
LFU近似于LRU:它使用几率计数器(称为莫里斯计数器),以便仅使用每一个对象几个位来估计对象访问频率,并结合一个衰减周期,以便使计数器随时间减小:在某些时候,咱们再也不但愿将密钥视为频繁访问的密钥,即便它们是过去的密钥也是如此,所以该算法能够适应访问模式的转变。

这些信息的采样方式与LRU发生的状况相似(如本文档前面的部分中所述),以选择驱逐候选人。

可是,与LRU不一样,LFU具备某些可调参数:例如,若是频繁访问的项目再也不被访问,应该将其下降多快?还能够调整Morris计数器范围,以使算法更好地适应特定的用例。

默认状况下,Redis 4.0配置为:

在大约一百万个请求时使计数器饱和。
每隔一分钟使计数器衰减一次。
这些应该是合理的值,而且已通过实验测试,可是用户可能但愿使用这些配置设置来选择最佳值。

有关如何调整这些参数的说明,能够redis.conf在源代码发行版的示例文件中找到,但简要地说,它们是:

lfu-log-factor 10
lfu-decay-time 1
衰减时间是显而易见的时间,它是在采样时发现计数器早于该值应衰减的分钟数。平均值的一个特殊值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文件自我记录注释中提供了更多信息。

因为LFU是一项新功能,所以与LRU相比,咱们将很是感谢您提供有关LFU在您的用例中的性能的反馈。

相关文章
相关标签/搜索