LRU算法总结
LRU算法总结
无论是哪一层次的缓存都面临一个同样的问题:当容量有限的缓存的空闲空间全部用完后,又有新的内容需要添加进缓存时,如何挑选并舍弃原有的部分内容,从而腾出空间放入这些新的内容。解决这个问题的算法有几种,如最近使用算法(LRU)、先进先出算法(FIFO)、最近最少使用算法(LFU)、非最近使用算法(NMRU)等,这些算法在不同层次的缓存上执行时拥有不同的效率和代价,需根据具体场合选择最合适的一种。
最近使用算法, 顾名思义,可以将其理解为如果数据最近被访问过,那么将来被访问的几率也很高。它的实现有多种方式,比如LRU、LRU-K、Two queues、Mutiple 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算法总结的更多相关文章
- Android图片缓存之Lru算法
前言: 上篇我们总结了Bitmap的处理,同时对比了各种处理的效率以及对内存占用大小.我们得知一个应用如果使用大量图片就会导致OOM(out of memory),那该如何处理才能近可能的降低oom发 ...
- 缓存淘汰算法--LRU算法
1. LRU1.1. 原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也 ...
- 借助LinkedHashMap实现基于LRU算法缓存
一.LRU算法介绍 LRU(Least Recently Used)最近最少使用算法,是用在操作系统中的页面置换算法,因为内存空间是有限的,不可能把所有东西都放进来,所以就必须要有所取舍,我们应该把什 ...
- LinkedHashMap实现LRU算法
LinkedHashMap特别有意思,它不仅仅是在HashMap上增加Entry的双向链接,它更能借助此特性实现保证Iterator迭代按照插入顺序(以insert模式创建LinkedHashMap) ...
- LinkedHashMap 和 LRU算法实现
个人觉得LinkedHashMap 存在的意义就是为了实现 LRU 算法. public class LinkedHashMap<K,V> extends HashMap<K,V&g ...
- 简单LRU算法实现缓存
最简单的LRU算法实现,就是利用jdk的LinkedHashMap,覆写其中的removeEldestEntry(Map.Entry)方法即可,如下所示: java 代码 import java.ut ...
- memached 服务器lru算法
1.LRU是Least Recently Used的缩写,即最近最少使用页面置换算法,是为虚拟页式存储管理服务的.LRU算法的提出,是基于这样一个事实:在前面几条指令中使用频繁的页面很可能在后面的几条 ...
- 用LinkedHashMap实现LRU算法
(在学习操作系统时,要做一份有关LRU和clock算法的实验报告,很多同学都应该是通过数组去实现LRU,可能是对堆栈的使用和链表的使用不是很熟悉吧,在网上查资料时看到了LinkedHashMap,于是 ...
- 近期最久未使用页面淘汰算法———LRU算法(java实现)
请珍惜小编劳动成果,该文章为小编原创,转载请注明出处. LRU算法,即Last Recently Used ---选择最后一次訪问时间距离当前时间最长的一页并淘汰之--即淘汰最长时间没有使用的页 依照 ...
- Android 图像压缩,和LRU算法使用的推荐链接
近两日,看的关于这些方面的一些教程数十篇,最好的当属google原版的教程了.国内有不少文章是翻译这个链接的. 需要注意的一点是:Android的SDK中的LRU算法在V4包和Util包中各有一个,推 ...
随机推荐
- 开涛spring3(12.1) - 零配置 之 12.1 概述
12.1 概述 12.1.1 什么是零配置 在SSH集成一章中大家注意到项目结构和包结构是不是很有规律,类库放到WEB-INF/lib文件夹下,jsp文件放到WEB-INF/jsp文件夹下,web ...
- centos 修改主机映射
[root@m1 ~]# vi /etc/hosts
- CSS3用法理解
这里只概括了我对CSS3各属性的用法理解.具体每个属性的值,以及例子,看这里 (竟然每篇文章不能低于200字,不能低于200字不能低于200字不能低于200字不能低于200字....请无视)
- R中用黏贴板读取excel数据以及用R生成人名
本人尚在超级菜鸟级别,中间有些代码写的很low,请不要见笑! 1.今天先跟大家分享一个小技巧,用黏贴板将excel中的数据读进R 首先选中要读入R的数据 如图,上面是我分析的病人相关资料,Ctrol+ ...
- 搭建免费wifi,嗅探接入该wifi的所有网络信息
环境: 1: create_ap , 搭建免费wifi. 2: wireshark , 嗅探网络信息. 搭建热点 搭建热点: git clone https://github.com/oblique/ ...
- javaSE_06Java中的数组(array)
1.什么是数组? 顾名思义,即为数据的组合或集合,数组就是用来表示一组数据的. 比如没有数组之前,我们要存储多个姓名的信息 String name1; String name2; String nam ...
- springmvc 添加@ResponseBody
1.添加ResponseBody之后的话 返回字符串的时候 就是一个字符串. @RequestMapping(value = "/{bookId}/detail.do",metho ...
- 多线程异步编程示例和实践-Thread和ThreadPool
说到多线程异步编程,总会说起Thread.ThreadPool.Task.TPL这一系列的技术.总结整理了一版编程示例和实践,分享给大家. 先从Thread和ThreadPool说起: 1. 创建并启 ...
- Chapter 5:Spectral-Subtractive Algorithms
作者:桂. 时间:2017-05-24 10:06:39 主要是<Speech enhancement: theory and practice>的读书笔记,全部内容可以点击这里. 书中 ...
- Nmap脚本文件分析(AMQP协议为例)
Nmap脚本文件分析(AMQP协议为例) 一.介绍 上两篇文章 Nmap脚本引擎原理 编写自己的Nmap(NSE)脚本,分析了Nmap脚本引擎的执行过程,以及脚本文件的编写,这篇文章将以解析AMQ ...