LRU(Least Recently Used)最近最少使用算法是众多置换算法中的一种。 
Redis中有一个maxmemory概念,主要是为了将使用的内存限定在一个固定的大小。Redis用到的LRU 算法,是一种近似的LRU算法。


1 设置maxmemory

上面已经说过maxmemory是为了限定Redis最大内存使用量。有多种方法设定它的大小。其中一种方法是通过CONFIG SET设定,如下:

127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "0"
127.0.0.1:6379> CONFIG SET maxmemory 100MB
OK
127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "104857600"

另一种方法是修改配置文件redis.conf

maxmemory 100mb

注意,在64bit系统下,maxmemory设置为0表示不限制Redis内存使用,在32bit系统下,maxmemory隐式不能超过3GB。 
当Redis内存使用达到指定的限制时,就需要选择一个置换的策略。


2 置换策略

当Redis内存使用达到maxmemory时,需要选择设置好的maxmemory-policy进行对老数据的置换。 
下面是可以选择的置换策略:

  • noeviction: 不进行置换,表示即使内存达到上限也不进行置换,所有能引起内存增加的命令都会返回error
  • allkeys-lru: 优先删除掉最近最不经常使用的key,用以保存新数据
  • volatile-lru: 只从设置失效(expire set)的key中选择最近最不经常使用的key进行删除,用以保存新数据
  • allkeys-random: 随机从all-keys中选择一些key进行删除,用以保存新数据
  • volatile-random: 只从设置失效(expire set)的key中,选择一些key进行删除,用以保存新数据
  • volatile-ttl: 只从设置失效(expire set)的key中,选出存活时间(TTL)最短的key进行删除,用以保存新数据

设置maxmemory-policy的方法和设置maxmemory方法类似,通过redis.conf或是通过CONFIG SET动态修改。

如果没有匹配到可以删除的key,那么volatile-lruvolatile-randomvolatile-ttl策略和noeviction替换策略一样——不对任何key进行置换。

选择合适的置换策略是很重要的,这主要取决于你的应用的访问模式,当然你也可以动态的修改置换策略,并通过用Redis命令——INFO去输出cache的命中率情况,进而可以对置换策略进行调优。

一般来说,有这样一些常用的经验:

  • 在所有的key都是最近最经常使用,那么就需要选择allkeys-lru进行置换最近最不经常使用的key,如果你不确定使用哪种策略,那么推荐使用allkeys-lru
  • 如果所有的key的访问概率都是差不多的,那么可以选用allkeys-random策略去置换数据
  • 如果对数据有足够的了解,能够为key指定hint(通过expire/ttl指定),那么可以选择volatile-ttl进行置换

volatile-lruvolatile-random经常在一个Redis实例既做cache又做持久化的情况下用到,然而,更好的选择使用两个Redis实例来解决这个问题。

设置是失效时间expire会占用一些内存,而采用allkeys-lru就没有必要设置失效时间,进而更有效的利用内存。


3 置换策略是如何工作的

理解置换策略的执行方式是非常重要的,比如:

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

我们持续的写数据会导致内存达到或超出上限maxmemory,但是置换策略会将内存使用降低到上限以下。

如果一次需要使用很多的内存(比如一次写入一个很大的set),那么,Redis的内存使用可能超出最大内存限制一段时间。


4 近似LRU算法

Redis中的LRU不是严格意义上的LRU算法实现,是一种近似的LRU实现,主要是为了节约内存占用以及提升性能。Redis有这样一个配置——maxmemory-samples,Redis的LRU是取出配置的数目的key,然后从中选择一个最近最不经常使用的key进行置换,默认的5,如下:

maxmemory-samples 5

可以通过调整样本数量来取得LRU置换算法的速度或是精确性方面的优势。

Redis不采用真正的LRU实现的原因是为了节约内存使用。虽然不是真正的LRU实现,但是它们在应用上几乎是等价的。下图是Redis的近似LRU实现和理论LRU实现的对比:

测试开始首先在Redis中导入一定数目的key,然后从第一个key依次访问到最后一个key,因此根据LRU算法第一个被访问的key应该最新被置换,之后再增加50%数目的key,导致50%的老的key被替换出去。 
在上图中你可以看到三种类型的点,组成三种不同的区域:

  • 淡灰色的是被置换出去的key
  • 灰色的是没有被置换出去的key
  • 绿色的是新增加的key

理论LRU实现就像我们期待的那样,最旧的50%数目的key被置换出去,Redis的LRU将一定比例的旧key置换出去。

可以看到在样本数为5的情况下,Redis3.0要比Redis2.8做的好很多,Redis2.8中有很多应该被置换出去的数据没有置换出去。在样本数为10的情况下,Redis3.0很接近真正的LRU实现。

LRU是一个预测未来我们会访问哪些数据的模型,如果我们访问数据的形式接近我们预想——幂律,那么近似LRU算法实现将能处理的很好。

在模拟测试中我们可以发现,在幂律访问模式下,理论LRU和Redis近似LRU的差距很小或者就不存在差距。

如果你将maxmemory-samples设置为10,那么Redis将会增加额外的CPU开销以保证接近真正的LRU性能,可以通过检查命中率来查看有什么不同。

通过CONFIG SET maxmemory-samples <count>动态调整样本数大小,做一些测试验证你的猜想。


本文主要参考:http://redis.io/topics/lru-cache

如何用Redis做LRU-Cache的更多相关文章

  1. 【转】如何用Redis做LRU-Cache

    LRU(Least Recently Used)最近最少使用算法是众多置换算法中的一种. Redis中有一个maxmemory概念,主要是为了将使用的内存限定在一个固定的大小.Redis用到的LRU ...

  2. 如何用redis做缓存

    redis缓存 在互联网应用中经常需要用redis来缓存热点数据. redis数据在内存,可以保证数据读取的高效,接近每秒数十万次的吞吐量 减少下层持久层数据库读取压力,像mongodb,每秒近千次读 ...

  3. Redis做LRU缓存

    当Redis用作缓存时,通常可以让它在添加新数据时自动逐出旧数据. 这种行为在开发人员社区中非常有名,因为它是流行的memcached系统的默认行为. LRU实际上只是支持的驱逐方法之一. 本页介绍了 ...

  4. 我是如何用redis做实时订阅推送的

    前阵子开发了公司领劵中心的项目,这个项目是以redis作为关键技术落地的.       先说一下领劵中心的项目吧,这个项目就类似京东app的领劵中心,当然图是截取京东的,公司的就不截了...   其中 ...

  5. 我是如何用redis做实时订阅推送的(转)

    前阵子开发了公司领劵中心的项目,这个项目是以redis作为关键技术落地的.       先说一下领劵中心的项目吧,这个项目就类似京东app的领劵中心,当然图是截取京东的,公司的就不截了...   其中 ...

  6. NetCore入门篇:(九)Net Core项目使用Session及用Redis做分布式

    一.简介 1.因为Net Core默认是没有启动Session功能的,如果需要使用,需要通过代码开启. 2.本篇说明如果启用默认Session实现,即Session存到内存中. 3.本篇扩展说明如何用 ...

  7. Redis(八) LRU Cache

    Redis(八)-- LRU Cache 在计算机中缓存可谓无所不在,无论还是应用还是操作系统中,为了性能都需要做缓存.然缓存必然与缓存算法息息相关,LRU就是其中之一.笔者在最先接触LRU是大学学习 ...

  8. [转]如何用C++实现一个LRU Cache

    [转自http://hawstein.com/posts/lru-cache-impl.html] LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法 ...

  9. 使用Redis做MyBatis的二级缓存

    使用Redis做MyBatis的二级缓存 通常为了减轻数据库的压力,我们会引入缓存.在Dao查询数据库之前,先去缓存中找是否有要找的数据,如果有则用缓存中的数据即可,就不用查询数据库了. 如果没有才去 ...

随机推荐

  1. 基于ARM-contexA9蜂鸣器驱动开发

    上次,我们写了一个LED的驱动程序,这一节,我们只需稍微改动一下就可以实现蜂鸣器的驱动,让我们来看看吧. 还是跟之前一样,先找电路图,找到电路板上对应的引脚和相关联的寄存器. 1.看电路图 (1)蜂鸣 ...

  2. 股票K线图

    代码链接地址:点击打开链接

  3. Java-ServletOutputStream

    /** * Provides an output stream for sending binary data to the * client. A <code>ServletOutput ...

  4. Java-ServletContextAttributeListener

    /** Implementations of this interface receive notifications of ** changes to the attribute list on t ...

  5. 苹果IOS与谷歌 android系统的UI设计原则

    一.苹果为IOS的界面设计提出了六大原则: 1.整体美学 整体美学指的是一个应用的表现和行为与它的功能完美集成,传达连贯的信息. 人们关心一个应用是否提供它承诺的功能,但他们也被应用的外观和行为强烈影 ...

  6. Android开发技巧——使用Dialog实现仿QQ的ActionSheet菜单

    最近看到有人用Dialog来实现QQ的仿ActionSheet的自定义菜单,对于自己没实现过的一些控件,看着也想实现一下.于是动手了一下,发现也不难,和大家分享一下. 本文原创,转载请注明出处:htt ...

  7. GraphCuts算法解析,Graphcuts算法求最大流,最小割实例

    图割论文大合集下载: http://download.csdn.net/detail/wangyaninglm/8292305 代码: /* graph.h */ /* Vladimir Kolmog ...

  8. Set对象常用操作方法和遍历

    Set<String> set = new HashSet<String>(); /** * set的常用操作方法有: * add()向集合添加元素 clear()清空集合元素 ...

  9. oracle超出打开游标的最大数的原因和解决方案

    oracle超出打开游标的最大数的原因和解决方案 分类: Oracle相关2012-06-05 10:36 6362人阅读 评论(0) 收藏 举报 oracle数据库sqljavasessionsys ...

  10. JS基础:this的指向以及call、apply的作用

    this 的指向 在具体的实际应用中,this 的指向无法在函数定义时确定,而是在函数执行的时候才确定的,根据执行时的环境大致可以分为以下3种: 1.当函数作为普通函数调用时,this 指向全局对象 ...