好久前参加过今日头条的面试,遇到一个题,目前半部分是如何实现 LRU,后半部分是 Redis 中如何实现 LRU。html
个人第一反应是操做系统课程里学过,应该是内存不够的场景下,淘汰旧内容的策略。LRU ... Least Recent Used,淘汰掉最不常常使用的。能够稍微多补充两句,由于计算机体系结构中,最大的最可靠的存储是硬盘,它容量很大,而且内容能够固化,可是访问速度很慢,因此须要把使用的内容载入内存中;内存速度很快,可是容量有限,而且断电后内容会丢失,而且为了进一步提高性能,还有CPU内部的 L1 Cache,L2 Cache等概念。由于速度越快的地方,它的单位成本越高,容量越小,新的内容不断被载入,旧的内容确定要被淘汰,因此就有这样的使用背景。node
在通常标准的操做系统教材里,会用下面的方式来演示 LRU 原理,假设内存只能容纳3个页大小,按照 7 0 1 2 0 3 0 4 的次序访问页。假设内存按照栈的方式来描述访问时间,在上面的,是最近访问的,在下面的是,最远时间访问的,LRU就是这样工做的。面试
可是若是让咱们本身设计一个基于 LRU 的缓存,这样设计可能问题不少,这段内存按照访问时间进行了排序,会有大量的内存拷贝操做,因此性能确定是不能接受的。redis
那么如何设计一个LRU缓存,使得放入和移除都是 O(1) 的,咱们须要把访问次序维护起来,可是不能经过内存中的真实排序来反应,有一种方案就是使用双向链表。算法
总体的设计思路是,可使用 HashMap 存储 key,这样能够作到 save 和 get key的时间都是 O(1),而 HashMap 的 Value 指向双向链表实现的 LRU 的 Node 节点,如图所示。缓存
LRU 存储是基于双向链表实现的,下面的图演示了它的原理。其中 h 表明双向链表的表头,t 表明尾部。首先预先设置 LRU 的容量,若是存储满了,能够经过 O(1) 的时间淘汰掉双向链表的尾部,每次新增和访问数据,均可以经过 O(1)的效率把新的节点增长到对头,或者把已经存在的节点移动到队头。app
下面展现了,预设大小是 3 的,LRU存储的在存储和访问过程当中的变化。为了简化图复杂度,图中没有展现 HashMap部分的变化,仅仅演示了上图 LRU 双向链表的变化。咱们对这个LRU缓存的操做序列以下:dom
save("key1", 7)post
save("key2", 0)性能
save("key3", 1)
save("key4", 2)
get("key2")
save("key5", 3)
get("key2")
save("key6", 4)
相应的 LRU 双向链表部分变化以下:
总结一下核心操做的步骤:
完整基于 Java 的代码参考以下
LRU Cache
那么问题的后半部分,是 Redis 如何实现,这个问题这么问确定是有坑的,那就是redis确定不是这样实现的。
若是按照HashMap和双向链表实现,须要额外的存储存放 next 和 prev 指针,牺牲比较大的存储空间,显然是不划算的。因此Redis采用了一个近似的作法,就是随机取出若干个key,而后按照访问时间排序后,淘汰掉最不常常使用的,具体分析以下:
为了支持LRU,Redis 2.8.19中使用了一个全局的LRU时钟,server.lruclock
,定义以下,
默认的LRU时钟的分辨率是1秒,能够经过改变REDIS_LRU_CLOCK_RESOLUTION
宏的值来改变,Redis会在serverCron()
中调用updateLRUClock
按期的更新LRU时钟,更新的频率和hz参数有关,默认为100ms
一次,以下,
server.unixtime
是系统当前的unix时间戳,当 lruclock 的值超出REDIS_LRU_CLOCK_MAX时,会从头开始计算,因此在计算一个key的最长没有访问时间时,可能key自己保存的lru访问时间会比当前的lrulock还要大,这个时候须要计算额外时间,以下,
Redis支持和LRU相关淘汰策略包括,
volatile-lru
设置了过时时间的key参与近似的lru淘汰策略allkeys-lru
全部的key均参与近似的lru淘汰策略当进行LRU淘汰时,Redis按以下方式进行的,
Redis会基于server.maxmemory_samples
配置选取固定数目的key,而后比较它们的lru访问时间,而后淘汰最近最久没有访问的key,maxmemory_samples的值越大,Redis的近似LRU算法就越接近于严格LRU算法,可是相应消耗也变高,对性能有必定影响,样本值默认为5。
看来,虽然一个简单的概念,在工业界的产品中,为了追求空间的利用率,也会采用权衡的实现方案。
传送门 https://zhuanlan.zhihu.com/p/34133067
原文:https://blog.csdn.net/hopeztm/article/details/79547052
关于linkedhashmap实现LRU:https://www.cnblogs.com/lzrabbit/p/3734850.html