redis的key过期删除策略和内存淘汰机制
一、key的过期删除策略
原由:一般情况下,在使用redis作缓存,对k设置过期时间,当过期时间到后,k还是占用内存的,并没有从内存中移除。
1.定时删除
在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除
优点:保证内存被尽快释放
缺点:1>若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key。
2>定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重
2.惰性删除
key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。
优点:删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步.
缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)
3.定期删除
每隔一段时间执行一次删除过期key操作
定期任务(serverCron)任务每隔一段时间就会运行一次,其中就包含清理过期key的任务,运行频率由配置文件中的hz参数来控制,取值范围1~500,默认是10,代表每秒运行10次。清理过程如下:
- 遍历所有的db
- 从db中设置了过期时间的key的集合中随机检查20个key
- 删除检查中发现的所有过期key
如果检查结果中25%以上的key已过期,则继续重复执行步骤2-3,否则继续遍历下一个db,调大hz将会提高redis定期任务的执行频率,如果你的redis中包含很多过期key的话,可以考虑将这个值调大,但要注意同时也会增加CPU的压力,redis作者建议这个值不要超过100。
优点: 1>通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用--处理"定时删除"的缺点.
2>定期删除过期key--处理"惰性删除"的缺点
缺点:1>在内存友好方面,不如"定时删除"
2>在CPU时间友好方面,不如"惰性删除"
redis是怎么采用的过期策略
- 惰性删除+定期删除
- 惰性删除流程:在进行get或setnx等操作时,先检查key是否过期,若过期,删除key,然后执行相应操作;若没过期,直接执行相应操作
- 定期删除流程:遍历每个数据库,检查当前库中的指定个数个key(默认是每个库检查20个key,注意相当于该循环执行20次,循环体时下边的描述),如果当前库中没有一个key设置了过期时间,直接执行下一个库的遍历,随机获取一个设置了过期时间的key,检查该key是否过期,如果过期,删除key,判断定期删除操作是否已经达到指定时长,若已经达到,直接退出定期删除。
二、内存淘汰机制
1. 不进行数据淘汰(No Eviction)
noeviction
:- 行为: 这是 Redis 的默认策略。当内存达到上限时,Redis 不会主动淘汰任何数据。
- 结果: 如果此时有写入请求(如
SET
,LPUSH
等),Redis 会返回错误(通常是(error) OOM command not allowed when used memory > 'maxmemory'
)。读取请求(如GET
)仍然可以正常执行。 - 适用场景: 适用于数据量小且能精确控制内存,或者绝对不允许丢失任何数据的场景。需要应用程序自己处理内存溢出错误。
2. 基于过期时间的淘汰策略(针对设置了 expire
的 Key)
这些策略只会在设置了过期时间(TTL)的键中选择淘汰目标。
-
volatile-lru
:- 行为: 从设置了过期时间的键中,使用最近最少使用(Least Recently Used, LRU)算法淘汰最久未被访问的键。
- 特点: 优先保证长期未访问的过期数据被淘汰,保留了经常访问的热点过期数据。
- 适用场景: 缓存场景,其中大部分需要缓存的数据都设置了过期时间,希望保留热点数据。
-
volatile-lfu
:- 行为: 从设置了过期时间的键中,使用最少频率使用(Least Frequently Used, LFU)算法淘汰访问频率最低的键。
- 特点: LFU 算法比 LRU 更能反映数据的长期访问热度,能更好地保留高频访问的数据,即使它们最近一次访问不是最新的。
- 适用场景: 缓存场景,数据访问模式存在明显的热点,且希望更精确地保留高频访问的过期数据。需要 Redis 4.0+。
-
volatile-ttl
:- 行为: 从设置了过期时间的键中,淘汰剩余生存时间(TTL)最短的键。
- 特点: 优先淘汰即将过期的数据。
- 适用场景: 当希望数据尽可能在过期前被使用,但内存紧张时优先清理“快过期”的数据。效果可能不如
volatile-lru
或volatile-lfu
理想。
-
volatile-random
:- 行为: 从设置了过期时间的键中,随机选择一个键进行淘汰。
- 特点: 实现简单,性能开销小,但淘汰没有基于访问模式,可能淘汰掉热点数据。
- 适用场景: 对淘汰算法要求不高,或者访问模式非常随机,且所有过期键价值相当的场景。
3. 基于所有键的淘汰策略(针对所有 Key,无论是否设置过期时间)
这些策略会考虑数据库中的所有键(无论是否设置了过期时间)。
-
allkeys-lru
:- 行为: 在所有键中,使用最近最少使用(LRU)算法淘汰最久未被访问的键。
- 特点: 会淘汰长期未访问的任何数据,包括没有设置过期时间的持久化数据。
- 适用场景: 缓存场景,其中部分数据可能未设置过期时间,但仍希望基于访问热度进行淘汰。这是非常常用的缓存策略。
-
allkeys-lfu
:- 行为: 在所有键中,使用最少频率使用(LFU)算法淘汰访问频率最低的键。
- 特点: 同样能更好地保留高频访问的任何数据。
- 适用场景: 缓存场景,数据访问热点明显,希望最精确地保留高频访问数据,无论其是否过期。需要 Redis 4.0+。
-
allkeys-random
:- 行为: 在所有键中,随机选择一个键进行淘汰。
- 特点: 实现简单,性能开销最小,但完全不考虑访问模式,可能淘汰掉重要的持久化数据。
- 适用场景: 数据价值相对均匀,或者对数据丢失不敏感,追求极致性能的场景。
三、其他模块对过期键的处理
生成RDB文件时
- 执行 save 或 bgsave 时 ,数据库键空间中的过期键不会被保存在RDB文件中。
载入RDB文件时
- master 载入RDB时,文件中的未过期的键会被正常载入,过期键则会被忽略。
- slave 载入 RDB 时,文件中的所有键都会被载入,当同步进行时,会和Master 保持一致。
AOF 文件写入时
- 数据库键空间的过期键的过期但并未被删除释放的状态会被正常记录到AOF文件中,当过期键发生释放删除时,DEL也会被同步到 AOF文件中去。
主从同步过期key
- Master 删除过期key之后,会向所有slave 服务器发送一个DEL命令,从服务器收到之后,会删除这些Key。
- Slave 在被动的读取过期键时,不会做出操作,而是继续返回该键,只有当Master 发送 DEL 通知来,才会删除过期键,这是统一、中心化的键删除策略,保证主从服务器的数据一致性。
推荐一个非常好用的工具集合:在线工具集合 - 您的开发助手