LRU算法总结

无论是哪一层次的缓存都面临一个同样的问题:当容量有限的缓存的空闲空间全部用完后,又有新的内容需要添加进缓存时,如何挑选并舍弃原有的部分内容,从而腾出空间放入这些新的内容。解决这个问题的算法有几种,如最近使用算法(LRU)、先进先出算法(FIFO)、最近最少使用算法(LFU)、非最近使用算法(NMRU)等,这些算法在不同层次的缓存上执行时拥有不同的效率和代价,需根据具体场合选择最合适的一种。

最近使用算法, 顾名思义,可以将其理解为如果数据最近被访问过,那么将来被访问的几率也很高。它的实现有多种方式,比如LRULRU-KTwo queuesMutiple queues等。

LRU

常用的实现是使用下图中的方式,往头部加入新的数据,如果该数据存在则将其放到头部,如果加入时已满,则从底部淘汰掉数据。

这种方式虽然简单,在频繁访问热点数据的时候效率高,但是它的缺点在于如果是偶尔的批量访问不同的数据时其命中率就会很低。比如我频繁的访问A,接着访问不同的数据直到A被淘汰,此时我再访问A,则不得不又再次把A加入到Cache中,显然这种方式是不合时宜的,因为A已经访问了很多次了,不应该将其淘汰而把一堆只访问一次的数据加入到Cache中。

LRU-K

上面的LRU只会将最近使用的一次加入到缓存,因此需要将其进行优化,变成缓存k次的才加入到缓存中,于是我们需要维护一个历史队列,纪录其数据对应的访问次数,其根据访问次数来进行淘汰,如果访问次数达到了k次才从历史队列中删除加入到缓存中,缓存按照LRU的规则来淘汰数据。

它的命中率要比LRU要高,但是因为需要维护一个历史队列,因此内存消耗会比LRU多。

实际应用中LRU-2是综合各种因素后最优的选择,LRU-3或者更大的K值命中率会高,但适应性差,需要大量的数据访问才能将历史访问记录清除掉。

Two queues(2Q)

和LRU-k类似,但不同的是,其有两个缓存队列,一个是FIFO队列,一个是LRU队列。如图所示,FIFO队列纪录只有访问一次的数据,并且按照FIFO的规则来淘汰数据,如果数据被第二次访问,则会加入到缓存中,缓存队列则按照LRU的规则来淘汰数据。

缺点和LRU-2一样。

Muti Queue(MQ)

MQ算法根据访问频率将数据划分为多个队列,不同的队列具有不同的访问优先级,其核心思想是:优先缓存访问次数多的数据。需要注意的是,如果一个优先级中的数据在一定的时间内没有被访问,则会降低其的优先级到低等级的队列中。

MQ的缺点在于纪录每个数据的访问时间,需要定时的扫描数据,其代价要比LRU高。

最后

最后给一个LRU的双链表实现,新访问的数据放到链表的头部,尾部则是最近最少使用的数据。

class Node(object):
def __init__(self, key=None, value=None, next=None, prev=None):
self.key = key
self.value = value
self.next = next
self.prev = prev
class LRUCache(object):
def __init__(self, capacity):
"""
:type capacity: int
"""
self.capacity = capacity
# single linked list with a head node
# always put new node to the tail
# also move the revisted node to the tail
self.head = Node()
self.tail = self.head
self.head.next = self.tail
# <key, node.prev>
self.hash_table = {}
def pop_front(self):
del self.hash_table[self.head.next.key]
p_next = self.head.next.next
self.head.next = p_next
# update the reference for new front node
self.hash_table[self.head.next.key] = self.head
def append(self, node):
self.hash_table[node.key] = self.tail
self.tail.next = node
self.tail = node
def move_to_end(self, prev):
node = prev.next
if node == self.tail:
return
# disconnect node
prev.next = node.next
node.next = None
self.hash_table[prev.next.key] = prev
# append node
self.append(node)
def get(self, key):
"""
:type key: int
:rtype: int
"""
if key not in self.hash_table:
return -1
prev = self.hash_table[key]
val = prev.next.value
self.move_to_end(prev)
return val
def put(self, key, value):
"""
:type key: int
:type value: int
:rtype: void
"""
if key in self.hash_table:
prev = self.hash_table[key]
prev.next.value = value
self.move_to_end(prev)
else:
self.append(Node(key, value))
if len(self.hash_table) > self.capacity:
self.pop_front()

LRU算法总结的更多相关文章

  1. Android图片缓存之Lru算法

    前言: 上篇我们总结了Bitmap的处理,同时对比了各种处理的效率以及对内存占用大小.我们得知一个应用如果使用大量图片就会导致OOM(out of memory),那该如何处理才能近可能的降低oom发 ...

  2. 缓存淘汰算法--LRU算法

    1. LRU1.1. 原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也 ...

  3. 借助LinkedHashMap实现基于LRU算法缓存

    一.LRU算法介绍 LRU(Least Recently Used)最近最少使用算法,是用在操作系统中的页面置换算法,因为内存空间是有限的,不可能把所有东西都放进来,所以就必须要有所取舍,我们应该把什 ...

  4. LinkedHashMap实现LRU算法

    LinkedHashMap特别有意思,它不仅仅是在HashMap上增加Entry的双向链接,它更能借助此特性实现保证Iterator迭代按照插入顺序(以insert模式创建LinkedHashMap) ...

  5. LinkedHashMap 和 LRU算法实现

    个人觉得LinkedHashMap 存在的意义就是为了实现 LRU 算法. public class LinkedHashMap<K,V> extends HashMap<K,V&g ...

  6. 简单LRU算法实现缓存

    最简单的LRU算法实现,就是利用jdk的LinkedHashMap,覆写其中的removeEldestEntry(Map.Entry)方法即可,如下所示: java 代码 import java.ut ...

  7. memached 服务器lru算法

    1.LRU是Least Recently Used的缩写,即最近最少使用页面置换算法,是为虚拟页式存储管理服务的.LRU算法的提出,是基于这样一个事实:在前面几条指令中使用频繁的页面很可能在后面的几条 ...

  8. 用LinkedHashMap实现LRU算法

    (在学习操作系统时,要做一份有关LRU和clock算法的实验报告,很多同学都应该是通过数组去实现LRU,可能是对堆栈的使用和链表的使用不是很熟悉吧,在网上查资料时看到了LinkedHashMap,于是 ...

  9. 近期最久未使用页面淘汰算法———LRU算法(java实现)

    请珍惜小编劳动成果,该文章为小编原创,转载请注明出处. LRU算法,即Last Recently Used ---选择最后一次訪问时间距离当前时间最长的一页并淘汰之--即淘汰最长时间没有使用的页 依照 ...

  10. Android 图像压缩,和LRU算法使用的推荐链接

    近两日,看的关于这些方面的一些教程数十篇,最好的当属google原版的教程了.国内有不少文章是翻译这个链接的. 需要注意的一点是:Android的SDK中的LRU算法在V4包和Util包中各有一个,推 ...

随机推荐

  1. struts2.1.6教程七、国际化

    尽管国际化不是重点内容,但是也有必要了解它的使用.在struts2中国际化有三种级别:分别是针对某个Action的action级别,针对package的package级别,针对webapp的webap ...

  2. java虚拟机学习-深入理解JVM(1)

    1   Java技术与Java虚拟机 说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成: Java编程语言.Java类文件格式.Java虚拟机和Java应 ...

  3. mongoose populate

    mongoose具备关系数据库一样的关联查询,通过在schema模型中设置ref属性,然后在查询时使用populate关键字,可以达到关联查询的目的. 以下内容参考了mongoose官方文档http: ...

  4. hdu4417

    hdu4417 题意 给定一个数列,每次查询一个区间,和一个值h,问区间内有多少个数小于等于h. 分析 二分数的个数,划分树求解判断是否满足条件,划分树求解的是第k小的数,那么前面k个数肯定不大于这个 ...

  5. javaScript 设计模式系列之二:适配器模式

    介绍 适配器模式将一个类的接口转接成用户所期待的,有助于避免大规模改写现有客户代码. In software engineering, the adapter pattern is a softwar ...

  6. HTML5之2D物理引擎 Box2D for javascript Games 系列 第三部分之创建图腾破坏者的关卡

    创建图腾破坏者的关卡 现在你有能力创建你的第一个游戏原型,我们将从创建图腾破坏者的级别开始. 为了展示我们所做事情的真实性,我们将流行的Flash游戏图腾破坏者的一关作为 我们模仿的对象.请看下面的截 ...

  7. oracle日志挖掘

    oracle日志挖掘是一种十分强大的数据恢复技术,只要你保障你的归档日志和重做日志是完整的,那么就可以将你的数据恢复到任何时刻.简单叙述一下日志挖掘的基本原理,然后进行一个简单的小实验. 日志挖掘时基 ...

  8. web 直播&即时聊天------阿里云、融云(三)

    经过前面的知识,基本已经把聊天室的功能搞定了,剩下的就是直播的问题了... 一如既往,阿里云的web demo也是少的可怜,只有一个web播放器(Prismplayer),所以这里主要就此播放器踩的坑 ...

  9. 宿主机共享文件夹给不同Linux虚拟机的方法

    一.Windows/Linux宿主机共享文件夹给VMWare中的Linux虚拟机 1.能安装vmware tools1)在vmware的ubuntu中安装vmware tools2)在vmware中开 ...

  10. CSS3学习系列之盒样式(一)

    盒的基本类型 在css中,使用display属性来定义盒的类型,总体上来说,css中的盒分为block类型与inline类型 inline-block类型 inline-block类型是在css2.1 ...