淘汰策略
当Redis内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换(swap)。交换会让Redis的性能急剧下降,对于访问量较大的Redis来说,这样的龟速存取效率基本上等于不可用。
在生产环境中我们是不允许Redis出现交换行为的,为了限制最大使用内存,Redis提供了配置参数maxmemory来限制内存超出期望大小。
当实际内存超出maxmemory时,Redis提供了几种可选策略(maxmemory-policy)来让用户自己决定该如何腾新的空间来继续提供读写服务。
- noeviction:不会继续服务写请求(del请求可以继续),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。
- volatile-lru:尝试淘汰设置了过期时间的key,最少使用的key优先被淘汰。没有设置过期时间的额key不会被淘汰。这样可以保证需要持久化的数据不会突然丢失。
- volatile-ttl:跟上面几乎一样,不过淘汰的策略不是LRU,而是比较key的剩余ttl的值,ttl越小越优先被淘汰。
- volatile-random:跟上面几乎一样,不过淘汰的key是过期key集合中随机的key。
- allkeys-lru:区别于volatile-lru,这个策略要淘汰的key对象是全体的key集合,而不只是过期的key集合。这意味着一些没有设置过期时间的key也会被淘汰。
- allkeys-random:跟上面几乎一样,不过淘汰的key是随机的key。
volatile-xxx策略只会针对带过期时间的key进行淘汰,allkeys-xxx策略会对所有的key进行淘汰。如果你只是拿Redis做缓存,那么应该使用allkeys-xxx策略,客户端写缓存时不必携带过期时间。如果你还想同时使用Redis的持久化功能,那就是用volatile-xxx策略,这样可以保留没有设置过期时间的key,他们是永久的key,不会被LRU算法淘汰。
LRU算法
实现LRU算法除了需要key、value字典外,还需要附加一个链表,链表中的元素按照一定的顺序进行排列。当空间满的时候,会踢掉链表尾部的元素。当字典的某个元素被访问时,它在列表中的位置会被移动到表头,所以链表的元素排列顺序就是元素最近被访问的时间顺序。
位于链表尾部的元素就是不被重用的元素,所以会被踢掉。位于表头的元素就是最近刚刚被人用过的元素,所以暂时不会被踢。
近似LRU算法
Redis使用的是一种近似LRU算法,它跟LRU算法不太一样,之所以不选用LRU算法是因为其需要消耗大量的额外内存,需要对现有的数据结构进行较大的改造。近似LRU算法很简单,在现有数据结构的基础上使用随机采样法来淘汰元素,能达到和LRU算法非常近似的效果。Redis为实现近似LRU算法,为每个key增加了一个额外的24bit长的小字段,也就是最后被访问的时间戳。
处理key过期方式分为几种处理和懒惰处理,LRU淘汰不一样,他的处理方式只有懒惰处理。当Redis执行写操作时,发现内存超过maxmemory,就会执行一次LRU淘汰算法。这个算法会随机采样出5(数量可以设置)个key,然后淘汰掉最旧的key,如果淘汰后仍然是超过maxmemory,那就继续进行淘汰,直到内存低于maxmemory为止。