当Redis内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换。交换会让Redis的性能急剧下降,对于访问量比较频繁的Redis来说等于不可用。
为了限制最大使用内存,Redis提供了配置参数maxmemory来限制内存超出期望大小。当实际内存超出maxmemory时,Redis 提供了几种可选策略来让用户自己决定该如何腾出新的空间以继续提供读写服务:
1.noeviction不会继续服务写请求 (DEL请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。
2.volatile-lru 尝试淘汰设置了过期时间的key,最少使用的 key优先被淘汰。没有设置过期时间的key不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。
3.volatile-ttl 跟上面一样,除了淘汰的策略是key的剩余寿命ttl的值,ttl越小越优先被淘汰。
4.volatile-random跟上面一样,不过淘汰的key是过期key集合中随机的key。
5.allkeys-lru区别于volatile-lru是要淘汰的key对象是全体的key集合,而不只是过期的key集合。这意味着没有设置过期时间的key也会被淘汰。
6.allkeys-random跟上面一样,不过淘汰的策略是随机的key。
总之,volatile-xxx策略只会针对带过期时间的key进行淘汰,allkeys-xxx策略会对所有的key进行淘汰。
Redis是怎么设计这个策略的呢?
答:Redis给每个key增加了一个额外的小字段,这个字段的长度是24个 bit,也就是最后一次被访问的时间戳。
Redis采取了近似于LRU算法,采用懒惰处理方式,当发现内存超出maxmemory,就会执行一次LRU淘汰算法。就是随机采样出5(可以配置)个key,然后淘汰掉最旧的key,如果淘汰后内存还是超出maxmemory,那就继续随机采样淘汰,直到内存低于maxmemory为止。
如何采样就是看 maxmemory-policy 的配置,如果是allkeys就是从所有的key字典中随机,如果是volatile就从带过期时间的 key 字典中随机。每次采样多少个key看的是 maxmemory_samples的配置,默认为5。
同时 Redis3.0在算法中增加了淘汰池,进一步提升了近似 LRU 算法的效果。淘汰池是一个数组,它的大小是maxmemory_samples,在每一次淘汰循环中,新随机出来的key列表会和淘汰池中的key 列表进行融合,淘汰掉最旧的一个key之后,保留剩余较旧的key列表放入淘汰池中留待下一个循环。