内存淘汰机制
当 Redis 内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换 (swap)。 交换会让 Redis 的性能急剧下降,对于访问量比较频繁的 Redis 来说,这样低速的存取效率基本上等于不可用。
在生产环境中我们是不允许 Redis 出现交换行为的,为了限制最大使用内存,Redis 提供了配置参数 maxmemory 来限制内存超出期望大小。
1.淘汰策略
当实际内存超出 maxmemory 时,Redis 提供了6种可选策略 (maxmemory-policy) 来让用户自己决定该如何腾出新的空间以继续提供读写服务。
- noeviction:不会继续服务写请求 (DEL 请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。
- volatile-lru:尝试淘汰设置了过期时间的 key,通过LRU算法驱逐最近最少使用的key。没有设置过期时间的 key 不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。
- volatile-random:尝试淘汰设置了过期时间的 key,在设置了过期时间的key集合中随机选择数据淘汰。
- volatile-ttl:尝试淘汰设置了过期时间的 key,在设置了过期时间的key集合中优先淘汰ttl小的。
- allkeys-lru:和volatile-lru的区别在于要淘汰的key对象是全体key集合而不只是设置了过期时间的key,其他都一样。
- allkeys-random:和volatile-random的区别在于要淘汰的key对象是全体key集合而不只是设置了过期时间的key,其他都一样。
Redis4.0后新增了两个策略:
volatile-lfu:尝试淘汰设置了过期时间的 key,通过LFU算法驱逐使用频率最少的key。没有设置过期时间的 key 不会被淘汰。
allkeys-lfu:和volatile-lfu的区别在于要淘汰的key对象是全体key集合而不只是设置了过期时间的key,其他都一样。
2.LRU算法
Redis LRU使用的是近似LRU算法,它跟 LRU 算法还不太一样。之所以不使用 LRU 算法,是因为需要消耗大量的额外的内存,需要对现有的数据结构进行较大的改造。近似 LRU 算法则很简单,在现有数据结构的基础上使用随机采样法来淘汰元素,能达到和 LRU 算法非常近似的效果。
Redis 为实现近似 LRU 算法,它给每个 key 增加了一个额外的小字段,这个字段长度24位,存的是最后一次被访问的时间戳,当Redis执行写操作时,发现内存超出我们配置的{maxmemory_samples}个样本,默认值为5,然后淘汰掉最旧的key,如果淘汰后内存还超出{maxmemory}为止。
采样数越大,近似LRU算法的效果越接近严格LRU算法,通过样本数量调整算法的精度
淘汰池是一个数组,它的大小是${maxmemory_samples},在每次淘汰循环中,新随机出的key列表会淘汰池中的key列表进行融合,淘汰掉最旧的一个key之后,保留剩余较旧的key列表放入淘汰池等待下一个循环。