博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LRU算法总结
阅读量:5098 次
发布时间:2019-06-13

本文共 3086 字,大约阅读时间需要 10 分钟。

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 = prevclass 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        # 
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()

转载于:https://www.cnblogs.com/George1994/p/7137007.html

你可能感兴趣的文章
无人值守安装linux系统
查看>>
黑马程序员——2 注释
查看>>
android dialog使用自定义布局 设置窗体大小位置
查看>>
ionic2+ 基础
查看>>
查询消除重复行
查看>>
[leetcode]Minimum Path Sum
查看>>
内存管理 浅析 内存管理/内存优化技巧
查看>>
Json格式的字符串转换为正常显示的日期格式
查看>>
Mobiscroll脚本破解,去除Trial和注册时间限制【转】
查看>>
Redis快速入门
查看>>
BootStrap---2.表格和按钮
查看>>
Ajax之404,200等查询
查看>>
Aizu - 1378 Secret of Chocolate Poles (DP)
查看>>
csv HTTP简单表服务器
查看>>
IO流写出到本地 D盘demoIO.txt 文本中
查看>>
Screening technology proved cost effective deal
查看>>
Redis Cluster高可用集群在线迁移操作记录【转】
查看>>
mysql8.0.13下载与安装图文教程
查看>>
Thrift Expected protocol id ffffff82 but got 0
查看>>
【2.2】创建博客文章模型
查看>>