Redis过期清除策略和内存淘汰机制
Table of Contents generated with DocToc (opens new window)
# 过期清除策略
如果我们设置了Redis的key-value的过期时间,当缓存中的数据过期之后,Redis就需要将这些数据进行清除,释放占用的内存空间。Redis中主要使用 定期删除 + 惰性删除 两种数据过期清除策略。
# 如何设置key的过期时间
设置key的过期时间:
(1) EXPIRE key seconds
// 设置多少秒后过期
(2) EXPIREAT key timestamp
//设置 key 过期时间的时间戳(unix timestamp) 以秒计
(3) PEXPIRE key milliseconds
// 设置多少毫秒后过期
(4) PEXPIREAT key milliseconds-timestamp
// 设置 key 过期时间的时间戳(unix timestamp) 以毫秒计
移除redis的过期时间:
PERSIST key
// 移除key的过期时间,key将保持永久
查询剩余生存时间:
TTL key
// 以秒为单位,返回给定 key 的剩余生存时间
PTTL key
// 以毫秒为单位返回 key 的剩余的过期时间
当key过期后,该key保存的数据还是会占据内存的,因为每当我们设置一个键的过期时间时,Redis会将该键带上过期时间存放到一个过期字典中。当key过期后,如果没有触发redis的删除策略的话,过期后的数据依然会保存在内存中的,这时候即便这个key已经过期,我们还是能够获取到这个key的数据。
# 三种过期删除策略
# 定时删除
在设置某个key 的过期时间同时,我们创建一个定时器,让定时器在该过期时间到来时,立即执行对其进行删除的操作。
优点:定时删除对内存是最友好的,能够保存内存的key一旦过期就能立即从内存中删除。
缺点:对CPU最不友好,在过期键比较多的时候,删除过期键会占用一部分 CPU 时间,对服务器的响应时间和吞吐量造成影响。
# 定期删除
每隔一段时间,我们就对一些key进行检查,删除里面过期的key。Redis默认每隔100ms就随机抽取部分设置了过期时间的key,检测这些key是否过期,如果过期了就将其删除。
这里有两点需要注意下:
- 默认的每隔100ms是在Redis的配置文件redis.conf中有一个属性"
hz
",默认为10,表示1s执行10次定期删除,即每隔100ms执行一次,可以修改这个配置的值来设置默认的间隔时间。
- 随机抽取部分,而不是全部key。因为如果Redis里面有大量key都设置了过期时间,全部都去检测一遍的话CPU负载就会很高,会浪费大量的时间在检测上面,甚至直接导致redis挂掉。所有只会抽取一部分而不会全部检查。
优点:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除,也能有效释放过期键占用的内存。
缺点:难以确定删除操作执行的时长和频率。如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好。如果执行的太少,那又和惰性删除一样了,过期键长时间占用的内存没有及时释放的话,当我们再次获取这个过期的key时,依然会返回这个key的值,就相当于这个过期时间是无效的了。
# 惰性删除
设置该key 过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key。
优点:对 CPU友好,我们只会在使用该键时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查。
缺点:对内存不友好,如果一个键已经过期,但是一直没有使用,那么该键就会一直存在内存中,如果数据库中有很多这种使用不到的过期键,这些键便永远不会被删除,内存永远不会释放,从而造成内存泄漏。所以redis还引入了另一种内存淘汰机制。
Redis默认采用的过期策略是定期删除与惰性删除结合
# 定期删除+惰性删除存在的问题
如果某个key过期后,定期删除没删除成功,然后也没再次去请求key,也就是说惰性删除也没生效。这时,如果大量过期的key堆积在内存中,redis的内存会越来越高,导致redis的内存块耗尽。那么此时就应该采用内存淘汰机制。
# 为什么要有淘汰机制
Redis 缓存使用内存保存数据,避免了系统直接从后台数据库读取数据,提高了响应速度。由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来。Redis 定义了「淘汰机制」用来解决内存被写满的问题。 缓存淘汰机制,也叫缓存替换机制,它需要解决两个问题:
- 决定淘汰哪些数据;
- 如何处理那些被淘汰的数据。
# 如何获取及设置内存淘汰策略
1、获取当前内存淘汰策略:
127.0.0.1:6379> config get maxmemory-policy
可以看到当前使用的默认的noeviction策略 2、获取Redis能使用的最大内存大小
127.0.0.1:6379> config get maxmemory
如果不设置最大内存大小或者设置最大内存大小为0,在64位操作系统下不限制内存大小,在32位操作系统下最多使用3GB内存。32 位的机器最大只支持 4GB 的内存,而系统本身就需要一定的内存资源来支持运行,所以 32 位机器限制最大 3 GB 的可用内存
3、设置淘汰策略 通过配置文件设置淘汰策略(修改redis.conf文件):
maxmemory-policy allkeys-lru
通过命令修改淘汰策略:
127.0.0.1:6379> config set maxmemory-policy allkeys-lru
4、设置Redis最大占用内存大小
#设置Redis最大占用内存大小为100M
127.0.0.1:6379> config set maxmemory 100mb
2
# Redis的内存淘汰策略
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
redis的缓存淘汰策略
# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key among the ones with an expire set.
# allkeys-random -> Remove a random key, any key.
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# noeviction -> Don't evict anything, just return an error on write operations.
#
# LRU means Least Recently Used
# LFU means Least Frequently Used
#
# Both LRU, LFU and volatile-ttl are implemented using approximated
# randomized algorithms.
# 省略
#
# The default is:
#
# maxmemory-policy noeviction
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Redis 4.0 之前一共实现了 6 种内存淘汰策略,在 4.0 之后,又增加了 2 种策略。截止目前,Redis定义了「8种内存淘汰策略」用来处理 redis 内存满的情况:
- noeviction: 不会淘汰任何数据,当使用的内存空间超过
maxmemory
值时,返回错误; - volatile-ttl:筛选设置了过期时间的键值对,越早过期的越先被删除;
- volatile-random:筛选设置了过期时间的键值对,随机删除;
- volatile-lru:使用
LRU
算法筛选设置了过期时间的键值对,移除最近最少使用的键值对; - volatile-lfu:使用
LFU
算法选择设置了过期时间的键值对,移除最近最不频繁使用的键值对; - allkeys-random:在所有键值对中,随机选择并删除数据;
- allkeys-lru:使用
LRU
算法在所有数据中进行筛选,移除最近最少使用的键值对; - allkeys-lfu:,使用
LFU
算法在所有数据中进行筛选,移除最近最不频繁使用的键值对。
上面是内存不足时的key「淘汰策略」,还有一种是过期键的删除策略,两者是不同的
1)、 noeviction
策略,也是 Redis 的默认策略,它要求 Redis 在使用的内存空间超过 maxmemory
值时,也不进行数据淘汰。一旦缓存被写满了,再有写请求来的时候,Redis 会直接返回错误。
我们实际项目中,一般不会使用这种策略。因为我们业务数据量通常会超过缓存容量的,而这个策略不淘汰数据,导致有些热点数据保存不到缓存中,失去了使用缓存的初衷。
2)、 我们再分析下 volatile-random
、volatile-ttl
、volatile-lru
、volatile-lfu
这四种淘汰策略。它们淘汰数据的时候,只会筛选设置了过期时间的键值对上。
比如,我们使用 EXPIRE
命令对一批键值对设置了过期时间,那么会有两种情况会对这些数据进行清理:
- 一种是过期时间到期了,会被删除;
- 一种是 Redis 的内存使用量达到了
maxmemory
阈值,Redis 会根据volatile-random
、volatile-ttl
、volatile-lru
、volatile-lfu
这四种淘汰策略,具体的规则进行淘汰;这也就是说,如果一个键值对被删除策略选中了,即使它的过期时间还没到,也需要被删除。
3)、 allkeys-random
,allkeys-lru
,allkeys-lfu
这三种策略跟上述四种策略的区别是:淘汰时数据筛选的数据范围是所有键值对。
我们按照是否会进行数据淘汰,以及根据淘汰数据集的筛选的范围进行总结,如下图:
通过我们上面的分析,volatile-random
、volatile-ttl
以及allkeys-random
的筛选规则比较简单,而 volatile-lru
,volatile-lfu
,allkeys-lru
,allkeys-lfu
分别用到了LRU
和 LFU
算法,我们继续分析。
# LRU算法
LRU
算法全称 Least Recently Used
,一种常见的页面置换算法。按照「最近最少使用」的原则来筛选数据,筛选出最不常用的数据,而最近频繁使用的数据会留在缓存中。
# LRU的筛选逻辑
LRU
会把所有的数据组织成一个链表,链表的头和尾分别表示 MRU
端和 LRU
端,分别代表「最近最常使用」的数据和「最近最不常用」的数据。
如下图
我们现在有数据 6、3、9、20、5。
数据 20 和 3 被先后访问,它们都会从现有的链表位置移到 MRU
端,而链表中在它们之前的数据则相应地往后移一位。
因为,LRU
算法选择删除数据时,都是从 LRU
端开始,所以把刚刚被访问的数据移到 MRU
端,就可以让它们尽可能地留在缓存中。
如果有一个新数据 15 要被写入缓存,但此时已经没有缓存空间了,也就是链表没有空余位置了,那么,LRU
算法会做两件事:
- 因为数据 15 是刚被访问的,所以它会被放到
MRU
端; - 算法把
LRU
端的数据 5 从缓存中删除,相应的链表中就没有数据 5 的记录了。
其实,LRU
的算法逻辑十分简单:它认为,刚刚被访问的数据,肯定还会被再次访问,所以就把它放在 MRU
端;LRU
端的数据被认为是长久不访问的数据,在缓存满时,就优先删除它。
我们可以把它理解为手机的后台应用窗口。它总是会把最近常用的窗口放在最前边,而不常用的应用窗口,就排列在后边了,如果再加上只能放置 N 个应用窗口的限制,淘汰最近最少用的应用窗口,那就是一个活生生的 LRU 了。
不过,LRU
算法在实际实现时,需要用「链表」管理所有的缓存数据,这会带来两个问题:
- 额外的空间开销;
- 当有数据被访问时,需要在链表上把该数据移动到 MRU 端,如果有大量数据被访问,就会带来很多链表移动操作,会很耗时,进而降低 Redis 缓存性能。
所以,在 Redis 中,LRU
算法被做了简化,「以减轻数据淘汰对缓存性能的影响」。
# Redis对LRU算法的实现
简单来说,Redis 默认会记录每个数据的最近一次访问的时间戳(由键值对数据结构 RedisObject 中的 lru 字段记录)。 然后,Redis 在决定淘汰的数据时,第一次会随机选出 N 个数据,把它们作为一个候选集合。 接下来,Redis 会比较这 N 个数据的 lru 字段,把 lru 字段值最小的数据从缓存中淘汰出去。
Redis 提供了一个配置参数 maxmemory-samples
,这个参数就是 Redis 选出的备选数据个数。
例如,我们执行如下命令,可以让 Redis 选出 100 个数据作为备选数据集:
config set maxmemory-samples 100
当需要再次淘汰数据时,Redis 需要挑选数据进入「第一次淘汰时创建的候选集合」。
挑选的标准是:能进入候选集合的数据的 lru 字段值必须小于「候选集合中最小的 lru 值」。
当有新数据进入备选数据集后,如果备选数据集中的数据个数达到了设置的阈值时。Redis 就把备选数据集中 lru 字段值最小的数据淘汰出去。
这样,Redis 缓存不用为所有的数据维护一个大链表,也不用在每次数据访问时都移动链表项,提升了缓存的性能。
# 小结
- 通常情况下推荐优先使用
allkeys-lru
策略。这样可以充分利用 LRU 这一经典缓存算法的优势,把最近最常访问的数据留在缓存中,提升应用的访问性能。 - 如果业务数据中「有明显的冷热数据区分」,建议使用
allkeys-lru
策略。这样,可以充分利用 LRU 算法的优势,把最近最常访问的数据留在缓存中,提升应用的访问性能。 - 如果业务应用中的「数据访问频率相差不大」,没有明显的冷热数据区分,建议使用
allkeys-random
策略,随机选择淘汰的数据。 - 如果业务中有「置顶」的需求,比如置顶新闻、置顶视频,那么,可以使用
volatile-lru
策略,同时不给这些置顶数据设置过期时间。这样一来,这些需要置顶的数据一直不会被删除,而其他数据会在过期时根据 LRU 规则进行筛选。 - 如果没有设置过期时间的键值对,那么 volatile-lru,volatile-lfu,volatile-random 和 volatile-ttl 策略的行为, 和 noeviction 基本上一致。
# LFU算法
LFU(Least Frequently Used),表示最近最少使用,它和key的使用次数有关,其思想是:根据key最近被访问的频率进行淘汰,比较少访问的key优先淘汰,反之则保留。
相比LRU算法,LFU增加了访问频率的这样一个维度来统计数据的热点情况,LFU主要使用了两个双向链表去形成一个二维的双向链表,一个用来保存访问频率,另一个用来访问频率相同的所有元素,其内部按照访问时间排序。
- 当添加元素的时候访问频次默认为1,于是找到相同频次的节点,然后添加到相同频率节点对应的双向链表的头部,
- 当元素被访问的时候就会增加对应key的访问频率,并且把访问的节点移动到下一个频次的节点。
LFU算法通过使用频率和上次访问时间来标记数据的这样一个热度,如果某个数据有读和写那么就增加访问的频率,如果一段时间内这个数据没有读写,那么就减少访问频率。
所以通过LFU算法改进之后,就可以真正达到非热点数据的淘汰,当然LFU也有缺点,相比LRU算法,LFU增加了访问频次的一个维护,以及实现的复杂度比LRU更高。
# 如何处理被淘汰的数据
一般来说,一旦被淘汰的数据选定后,如果这个数据是干净的,那么我们就直接删除;如果这个数据是脏数据,我们需要把它写回数据库。
干净数据和脏数据的区别就在于,和最初从后端数据库里读取时的值相比,有没有被修改过。 干净数据一直没有被修改,所以后端数据库里的数据也是最新值。在替换时,它可以被直接删除。而脏数据则相反。
但是,对于 Redis 来说,它决定了被淘汰的数据后,会把它们直接删除。即使淘汰的数据是脏数据,Redis 也不会把它们写回数据库。 所以,我们在使用 Redis 缓存时,如果数据被修改了,需要在数据修改时就将它写回数据库。 否则,这个脏数据被淘汰时,会被 Redis 删除,而数据库里也没有最新的数据了。
分析完 LRU
我们继续分析 LFU
算法。在这之前先了解一个问题:缓存污染。
# 缓存污染
在一些场景下,有些数据被访问的次数非常少,甚至只会被访问一次。当这些数据服务完访问请求后,如果还继续留存在缓存中的话,就只会白白占用内存空间。这种情况,就是**「缓存污染」**。 缓存污染一旦变得严重,就会有大量不再访问的数据滞留在缓存中。如果这些数据占满了缓存空间,我们再往缓存中写入新数据时,就需要先把这些数据逐步淘汰出缓存,这就会引入额外的内存消耗,进而会影响应用的性能。
# 如何解决缓存污染问题
解决方案,那就是得把不会再被访问的数据筛选出来并淘汰掉。这样就不用等到缓存被写满以后,再逐一淘汰旧数据之后,才能写入新数据了。 至于哪些淘汰哪些数据,是由缓存的淘汰策略决定的。 上面分析了 Redis 的 8 种淘汰策略,下面我们一一分析,这些策略对于解决缓存污染问题,是否都有效呢?
# volatile-random 和 allkeys-random
这两种策略,它们都是随机选择内存的数据进行淘汰。既然是随机挑选,那么 Redis 就不会根据「数据的访问情况」来筛选数据。 而且如果被淘汰的数据再次被访问了,就会发生缓存缺失。应用需要到后端数据库中访问这些数据,降低了应用的请求响应速度。 如图:
所以,volatile-random
和 allkeys-random
策略,在避免缓存污染这个问题上的效果非常有限。
# volatile-ttl 策略
volatile-ttl
筛选的是设置了过期时间的数据,把这些数据中剩余存活时间最短的筛选出来并淘汰。
虽然 volatile-ttl
策略不再是随机选择淘汰数据了,但是剩余存活时间并不能直接反映数据再次访问的情况。
所以,按照 volatile-ttl
策略淘汰数据,和按随机方式淘汰数据类似,也可能出现数据被淘汰后,被再次访问导致的缓存缺失问题。
有一种情况例外:业务应用在给数据设置过期时间的时候,明确知道数据被再次访问的情况,并根据访问情况设置过期时间。 此时,Redis 按照数据的剩余最短存活时间进行筛选,是可以把不会再被访问的数据筛选出来的,进而避免缓存污染。
小结:
- 在明确知道数据被再次访问的情况下,
volatile-ttl
可以有效避免缓存污染; - 在其他情况下,
volatile-random
、allkeys-random
、volatile-ttl
这三种策略不能应对缓存污染问题。
接下来,我们再分别分析 LRU
策略,以及 Redis 4.0 后实现的 LFU
策略。LRU
策略会按照数据访问的时效性,来筛选即将被淘汰的数据,应用非常广泛。
# LRU 策略
LRU 策略的核心思想:如果一个数据刚刚被访问,那么这个数据肯定是热数据,还会被再次访问。
Redis 中的 LRU 策略,会在每个数据对应的 RedisObject
结构体中设置一个 lru 字段
,用来记录数据的访问时间戳。
在进行数据淘汰时,LRU 策略会在候选数据集中淘汰掉 lru 字段值最小的数据,也就是最久不被访问的数据。
所以,在数据被频繁访问的业务场景中,LRU 策略的确能有效留存访问时间最近的数据。而且,因为留存的这些数据还会被再次访问,所以又可以提升应用的访问速度。
但是,也正是因为 只看数据的访问时间,使用 LRU 策略在处理「扫描式单次查询」操作时,无法解决缓存污染。
所谓的「扫描式单次查询操作」,就是指应用对大量的数据进行一次全体读取,每个数据都会被读取,而且只会被读取一次。 此时,因为这些被查询的数据刚刚被访问过,所以 lru 字段值都很大。
在使用 LRU 策略淘汰数据时,这些数据会留存在缓存中很长一段时间,造成缓存污染。
如果查询的数据量很大,这些数据占满了缓存空间,却又不会服务新的缓存请求。
此时,再有新数据要写入缓存的话,需要先把这些旧数据淘汰掉才行,这会影响缓存的性能。
举个简单例子。 如下图,数据 6 被访问后,被写入 Redis 缓存。但是,在此之后,数据 6 一直没有被再次访问,这就导致数据 6 滞留在缓存中,造成了污染。
所以,对于采用了 LRU 策略的 Redis 缓存来说,「扫描式单次查询」会造成缓存污染。 为了应对这类缓存污染问题,Redis 从 4.0 版本开始增加了 LFU 淘汰策略。
与 LRU 策略相比,LFU 策略中会从两个维度来筛选并淘汰数据:
- 一是,数据访问的时效性(访问时间离当前时间的远近);
- 二是,数据的被访问次数。
# LFU策略
LFU
缓存策略是在 LRU
策略基础上,为每个数据增加了一个「计数器」,来统计这个数据的访问次数。
# LFU策略的筛选规则
- 当使用 LFU 策略筛选淘汰数据时,首先会根据数据的访问次数进行筛选,把访问次数最低的数据淘汰出缓存。
- 如果两个数据的访问次数相同,LFU 策略再比较这两个数据的访问时效性,把距离上一次访问时间更久的数据淘汰出缓存。
# LFU 策略具体实现
我们上面提到,为了避免操作链表的开销,Redis 在实现 LRU
策略时使用了两个近似方法:
- Redis 在 RedisObject结构中设置了 lru 字段,用来记录数据的访问时间戳;
- Redis 并没有为所有的数据维护一个全局的链表,而是通过「随机采样」方式,选取一定数量的数据放入备选集合,后续在备选集合中根据 lru 字段值的大小进行筛选删除。
在此基础上,Redis 在实现 LFU
策略的时候,只是把原来 24bit 大小的 lru 字段,又进一步拆分成了两部分:
- ldt 值:lru 字段的前 16bit,表示数据的访问时间戳;
- counter 值:lru 字段的后 8bit,表示数据的访问次数。
举个简单例子。 假设第一个数据 A 的累计访问次数是 256,访问时间戳是 202010010909,所以它的 counter 值为 255。
Redis 只使用了 8bit 记录数据的访问次数,而 8bit 记录的最大值是 255。
而第二个数据 B 的累计访问次数是 1024,访问时间戳是 202010010810。
如果 counter 值只能记录到 255,那么数据 B 的 counter 值也是 255。
此时,缓存写满了,Redis 使用 LFU 策略进行淘汰。
由于数据 A 和 B 的 counter 值都是 255,LFU 策略会继续比较 A 和 B 的访问时间戳。发现数据 B 的上一次访问时间早于 A,就会把 B 淘汰掉。
但其实数据 B 的访问次数远大于数据 A,很可能会被再次访问。这样一来,使用 LFU 策略来淘汰数据就不合适了。
Redis 对此也进行了优化:在实现 LFU 策略时,Redis 并没有采用数据每被访问一次,就给对应的 counter 值加 1 的计数规则,而是采用了一个更优化的计数规则。
# Redis对LFU算法的实现
Redis 实现 LFU 策略的计数规则:
- 每当数据被访问一次时,先用「计数器当前的值」乘以「配置项 」
lfu_log_factor
,再加 1;取其倒数,得到一个 p 值; - 然后,把这个 p 值和一个取值范围在(0,1)间的随机数 r 值比大小,只有 p 值大于 r 值时,计数器才加 1。
下面是Redis 的部分源码,其中,baseval
是计数器当前的值。计数器的初始值默认是 5(由代码中的 LFU_INIT_VAL
常量设置),而不是 0。这样可以避免数据刚被写入缓存,就因为访问次数少而被立即淘汰。
double r = (double)rand()/RAND_MAX;
...
double p = 1.0/(baseval*server.lfu_log_factor+1);
if (r < p) counter++;
2
3
4
使用了这种计算规则后,我们可以通过设置不同的 lfu_log_factor
配置项,来控制计数器值增加的速度,避免 counter 值很快就到 255 了。
Redis 官网上提供的一张表,进一步说明 LFU 策略计数器递增的效果。它记录了当 lfu_log_factor
取不同值时,在不同的实际访问次数情况下,计数器值的变化情况。
可以看到,当 lfu_log_factor
取值为 1 时,实际访问次数为 100K 后,counter
值就达到 255 了,无法再区分实际访问次数更多的数据了。而当 lfu_log_factor
取值为 100 时,当实际访问次数为 10M 时,counter
值才达到 255。
正是因为使用了非线性递增的计数器方法,即使缓存数据的访问次数成千上万,LFU
策略也可以有效的区分不同的访问次数,从而合理的进行数据筛选。
从刚才的表中,我们可以看到,当 lfu_log_factor
取值为 10 时,百、千、十万级别的访问次数对应的 counter
值已经有明显的区分了。所以,我们在应用 LFU
策略时,一般可以将 lfu_log_factor
取值为 10。
前面我们也提到了,应用负载的情况是很复杂的。比如某些业务场景,有些数据在「短时间内被大量访问后就不会再被访问了」。那么再按照访问次数来筛选的话,这些数据会被留存在缓存中,但不会提升缓存命中率。
为此,Redis 在实现 LFU
策略时,还设计了一个「 counter 值的衰减机制」。
# counter 值的衰减机制
简单来说,LFU 策略使用「衰减因子配置项」 lfu_decay_time
来控制访问次数的衰减。
- LFU 策略会计算当前时间和数据最近一次访问时间的差值,并把这个差值换算成以分钟为单位。
- 然后,LFU 策略再把这个差值除以
lfu_decay_time
值,所得的结果就是数据 counter 要衰减的值。
简单举个例子,假设 lfu_decay_time
取值为 1,如果数据在 N 分钟内没有被访问,那么它的访问次数就要减 N。
如果 lfu_decay_time
取值更大,那么相应的衰减值会变小,衰减效果也会减弱。所以,如果业务应用中有短时高频访问的数据的话,建议把 lfu_decay_time
值设置为 1,这样一来,LFU 策略在它们不再被访问后,会较快地衰减它们的访问次数,尽早把它们从缓存中淘汰出去,避免缓存污染。
# 使用 LFU 策略会保证缓存不被污染吗?
在一些极端情况下,LFU 策略使用的计数器可能会在短时间内达到一个很大值,而计数器的「衰减配置项」 lfu_decay_time
设置得较大,导致计数器值衰减很慢,在这种情况下,数据就可能在缓存中长期驻留。所以不能一定保证缓存不会被污染。