Redis的内存淘汰策略以及LRU和LFU

关键字:maxmemory、LRU、LFU、Eviction policies、如何选择最合适的淘汰策略?


把Redis当做一个缓存来用的时候(Redis也可以作为持久化的数据库来使用),在内存不足时或者超过配置的上限时,它会删除一些旧的数据让新的数据能够被添加到缓存中,这是作为缓存部件最基本的功能。

Redis可以使用的内存大小是这么规定的:32位版本默认是3GB,而64位默认不限制。但是,在实际生产中,建议对它进行最大值配置,可通过在redis.conf中增加配置:

maxmemory 500mb

或者在Redis运行时,通过一下命令来:

config set maxmemory 500mb

当然如果配置为0,则是不限制内存大小(不建议如此)。

一旦内存使用到达上限,那么我们就得采用不同的内存淘汰策略:

  1. noeviction:默认。不淘汰旧数据,新的请求在无法分派到空间时,会返回异常。注意,读请求是不受影响的。
  2. allkeys-lru:least recently used,在所有的键值中,使用【近似LRU算法】进行淘汰
  3. allkeys-random:在所有的键值中,随机淘汰。
  4. volatile-lru:在设置了expire的键值中,使用【近似LRU算法】进行淘汰。
  5. volatile-random:在设置了expire的键值中,随机淘汰。
  6. volatile-ttl:shorter time to live有效期最小的数据

其中,volatile-lru, volatile-lfu, volatile-ttl在没有键值对设置了expire的情况下,会跟noeviction一样,空间不足时返回异常。

根据应用程序的访问模式来决定淘汰策略是很重要的,当然,淘汰策略可以在运行时随时修改。我们随时可以借助INFO命令来查看命中率,然后调整我们的策略。一般来说:

  • allkeys-lru适合那些符合幂律分布(简单地理解为二八法则)的数据,即少量一部分数据的访问频次远远比其他数据要高。
  • allkeys-random适用于所有的数据的访问频次相差不大。
  • volatile-ttl,当你希望通过expire来控制淘汰策略的时候,expire越小,越被早淘汰。
  • volatile-lru, volatile-random,当你的一个Redis实例即作为缓存也作为数据持久库的时候,可以使用这两种策略。当然,老叟更建议用两个Redis实例来分别做缓存和数据持久化【注1】。

另,设置expire是要占空间的,所以使用allkeys-lru的场景下,不需要设置expire,这使得空间使用率更高。

淘汰的过程到底是怎么样的呢?

  1. 客户端发来一个写命令,将数据保存。
  2. Redis检查空间使用情况,如果当前使用空间大于限制,那么会根据淘汰策略去淘汰键值。
  3. 执行下一条命令。

从以上过程可以看出,内存的使用是会超过我们所配置的限制的,一旦超过,则会去执行淘汰,让内存的使用在限制值之内。如果该命令会占用很大的内存,那么Redis会在很短的时间之内(在淘汰之前)会超过限制很多。

近似LRU算法

为了节省内存,Redis中的LRU算法并不是严格的LRU算法,意味着,Redis并不具备找到最合适的淘汰的对象的能力,即被访问过最久远的对象。Redis实现的是近似LRU算法,通过采样的方式,在样本中选取最合适淘汰对象(with the olded access time)。

在3.0版本中,改善了这个算法,使得这个算法更加接近于真正的LRU算法,还可以更改maxmemory-samples来调整这个算法的精确性。

下图是官方给出的算法对比图(https://redis.io/topics/lru-cache

 

其中:

  • 每个点(淡灰色的,灰色的,蓝色的)代表者一个键值对
  • 淡灰色和灰色是沾满了内存的键值对
  • 绿色是新加入的键值对,会触发allkeys-lru策略去淘汰键值对
  • 淡灰色是被淘汰的键值对

测试过程是这样子的,往Redis中存入满员的键值对(假设有10M),键值对按照插入先后顺序依次被访问过,对LRU算法来说,相当于第一个键值对是最符合的淘汰对象。接着,我们再插入5M的键值对,然后观察哪5M的键值对会被淘汰。

  • 左上图是理论上的LRU算法的效果图,严格按照Least Recenty Used来淘汰对象。
  • 左下图是Redis早期版本中的近似LRU算法,你会发现,有部分绿色的键值对也会被淘汰。
  • 右下图是Redis3.0版本之后改进的近似LRU算法,其中样本数是5的效果,更加接近LRU。
  • 右上图是样本数是10的效果,相对上述两个来说最接近真是的LRU。

所以,如果缓存中的数据符合幂律分布,那么LRU算法可以很好的处理键值对的淘汰问题。

样本数默认是5(config get maxmemory-samples),当然你可以调整该样本数的大小来观察命中率,值越大,效果越接近真实的LRU,但是代价是性能会有所消耗。大量的测试表明(官方说法),样本数为5,而且数据符合幂律分布的情况下,Redis的近似LRU几乎已经等价于真是的LRU。

LRU算法的实现,可以去看看Mybatis中的org.apache.ibatis.cache.decorators.LruCache,它使用了LinkedHashMap(accessOrder=true,并且重写了removeEldestEntry方法)来实现。

近似LFU算法

LRU算法有个缺点,比如在准备执行LRU之前,有个很少用到的键值对被访问了一次,那么在LRU算法下,它被淘汰的概率很小,比热点数据的淘汰概率还要小。所以在Redis4.0版本新增了近似LFU(Lest Frequently Used eviction mode)策略。

  • volatile-lfu:在设置了expire的键值对中使用近似LFU算法进行淘汰。
  • allkeys-lfu:在所有的键值对中使用近似LFU算法进行淘汰。

LFU算法是淘汰一段时间内最少被访问到的键值对,它使用了Approximate counting algorithm,使用很少的内存就可以估算一个键值对被访问到的概率。

注:

  1. Redis作为持久化数据库,为了保证数据不丢失,会配置AOF(always)+ RDB。而当作为缓存的时候,为了更高的性能,一般不配置AOF,而是看情况来配置RDB。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值