Redis的内存淘汰策略

长时间不使用的缓存

降低IO性能

物理内存不够

其实在Redis中是可以设置内存最大限制的,因此我们不用担心Redis占满机器的内存影响其他服务,这个参数maxmemory是可以配置的:

127.0.0.1:6379> config set maxmemory 1GB

maxmemory参数默认值为0。我们在使用redis 最好根据实际情况设置其内存大小。放在。redis占用太多内存,导致其他程序不可用。

内存淘汰策略

  1.内存没满,但是key过期了 怎么淘汰的?

    过期删除策略 进行淘汰

  2.内存满了怎么淘汰的

    LRU LFU 进行淘汰
Redis中共有下面六种内存淘汰机制:

noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了(redis默认的)。
allkeys-Iru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key,这个
一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。
volatile-Iru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近
最少使用的 key(这个一般不太合适)。
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机
移除某个key。
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。

内存淘汰算法的具体工作原理是:

客户端执行一条新命令,导致数据库需要增加数据(比如set key value)
Redis会检查内存使用,如果内存使用超过 maxmemory,就会按照置换策略删除一些 key
新的命令执行成功

了解LRU算法

LRU是Least Recently Used的缩写,也就是表示最近很少使用,也可以理解成最久没有使用。也就是说

当内存不够的时候,每次添加一条数据,都需要抛弃一条最久时间没有使用的旧数据。

标准的LRU算法为了降低查找和删除元素的时间复杂度,一般采用Hash表和双向链表结合的数据结构,

hash表可以赋予链表快速查找到某个key是否存在链表中,同时可以快速删除、添加节点,如图4-21所

示。

双向链表的查找时间复杂度是O(n),删除和插入是O(1),借助HashMap结构,可以使得查找的时
间复杂度变成O(1)

 

Hash表用来查询在链表中的数据位置,链表负责数据的插入,当新数据插入到链表头部时有两种情况。

链表满了,把链表尾部的数据丢弃掉,新加入的缓存直接加入到链表头中。
当链表中的某个缓存被命中时,直接把数据移到链表头部,原本在头节点的缓存就向链表尾部移动

这样,经过多次Cache操作之后,最近被命中的缓存,都会存在链表头部的方向,没有命中的,都会在链表尾部方向,

当需要替换内容时,由于链表尾部是最少被命中的,我们只需要淘汰链表尾部的数据即可。

Redis的内存淘汰 redis 内存淘汰_链表

 

 

Redis中的LRU算法

实际上,Redis使用的LRU算法其实是一种不可靠的LRU算法,它实际淘汰的键并不一定是真正最少使用

的数据,它的工作机制是:

随机采集淘汰的key,每次随机选出5个key(官方推荐5个就足够了,最多不超过10个,越大就越消耗CPU的资源)
然后淘汰这5个key中最少使用的key

这5个key是默认的个数,具体的数值可以在redis.conf中配置

maxmemory-samples 5

当近似LRU算法取值越大的时候就会越接近真实的LRU算法,因为取值越大获取的数据越完整,淘汰中

的数据就更加接近最少使用的数据。这里其实涉及一个权衡问题,

如果需要在所有的数据中搜索最符合条件的数据,那么一定会增加系统的开销,Redis是单线程的,所

以耗时的操作会谨慎一些。

为了在一定成本内实现相对的LRU,早期的Redis版本是基于采样的LRU,也就是放弃了从所有数据中搜

索解改为采样空间搜索最优解。Redis3.0版本之后,Redis作者对于基于采样的LRU进行了一些优化:

Redis中维护一个大小为16的候选池,当第一次随机选取采用数据时,会把数据放入到候选池中,
并且候选池中的数据会更具时间进行排序。
当第二次以后选取数据时,只有小于候选池内最小时间的才会被放进候选池。
当候选池的数据满了之后,那么时间最大的key就会被挤出候选池。当执行淘汰时,直接从候选池
中选取最近访问时间小的key进行淘汰。

LFU算法

LFU(Least Frequently Used),表示最近最少使用,它和key的使用次数有关,其思想是:根据key最
近被访问的频率进行淘汰,比较少访问的key优先淘汰,反之则保留。

LRU的原理是使用计数器来对key进行排序,每次key被访问时,计数器会增大,当计数器越大,意味着

当前key的访问越频繁,也就是意味着它是热点数据。 它很好的解决了LRU算法的缺陷:一个很久没有

被访问的key,偶尔被访问一次,导致被误认为是热点数据的问题。

LFU的实现原理如图4-23所示,LFU维护了两个链表,横向组成的链表用来存储访问频率,每个访问频

率的节点下存储另外一个具有相同访问频率的缓存数据。具体的工作原理是:

当添加元素时,找到相同访问频次的节点,然后添加到该节点的数据链表的头部。如果该数据链表
满了,则移除链表尾部的节点
当获取元素或者修改元素是,都会增加对应key的访问频次,并把当前节点移动到下一个频次节
点。

添加元素时,访问频率默认为1,随着访问次数的增加,频率不断递增。而当前被访问的元素也会

随着频率增加进行移动。

Redis的内存淘汰 redis 内存淘汰_数据_02

 

TTL淘汰

Redis 数据集数据结构中保存了键值对过期时间的表,即 redisDb.expires。与 LRU 数据淘汰机制类似,
TTL 在设置了过期时间的key中,淘汰过期时间剩余最短的。

 

过期删除策略

前面介绍的LRU和LFU算法都是在Redis内存占用满的情况下的淘汰策略,那么当内存没占满时在Redis中过期的key是如何从内存中删除以达到优化内存占用的呢?

在Redis中过期的key不会立刻从内存中删除,而是会同时以下面两种策略进行删除:

惰性删除:当key被访问时检查该key的过期时间,若已过期则删除;已过期未被访问的数据仍保持在内存中,消耗内存资源;
定期删除:默认每隔100ms时间,随机检查设置了过期的key并删除已过期的key;维护定时器消耗CPU资源;

Redis每10秒进行一次过期扫描:

1.随机取20个设置了过期策略的key;
2.检查20个key中过期时间中已过期的key并删除;
3.如果有超过25%的key已过期则重复第一步;

这种循环随机操作会持续到过期key可能仅占全部key的25%以下时,并且为了保证不会出现循环过多的情况,默认扫描时间不会超过25ms;

AOF和RDB的过期删除策略

前面介绍了Redis的持久化策略RDB和AOF,当Redis中的key已过期未删除时,如果进行RDB和AOF的持久化操作时候会怎么操作呢?

在RDB持久化模式中我们可以使用save和bgsave命令进行数据持久化操作
在AOF持久化模式中使用rewriteaof和bgrewriteaof命令进行持久化操作

这四个命令都不会将过期key持久化到RDB文件或AOF文件中,可以保证重启服务时不会将过期key载入Redis。

为了保证一致性,在AOF持久化模式中,当key过期时候,会同时发送DEL命令给AOF文件和所有节点;

从节点不会主动的删除过期key除非它升级为主节点或收到主节点发来的DEL命令;