LRU缓存及实现
一、淘汰策略
缓存:缓存作为一种平衡高速设备与低速设备读写速度之间差异而引入的中间层,利用的是局部性原理。比如一条数据在刚被访问过只有就很可能再次被访问到,因此将其暂存到内存中的缓存中,下次访问不用读取磁盘直接从内存中的缓存读取。而内存是有限的,无法无限制的添加数据。当缓存超过设置的容量的时候,在添加缓存就需要选择性的移除无效数据。需要具体的策略判定数据是否无效。
1、FIFO
FIFO:First In First Out,先进先出,淘汰缓存中最早添加的数据。认为缓存中最早添加的数据被在此使用的可能性就越小。实现可以使用一个队列,队列中的数据严格遵循先进先出,每次内存不够用,则直接淘汰队首元素。但是很多场景下,最早添加的元素也会被经常访问,因此这类数据会频繁的进出缓存,导致性能不佳。
2、LFU
LFU:Least Frequently Used,最少使用,淘汰缓存中使用频率最低的数据。认为数据过去访问的次数越多,将来更可能被访问,因此应该尽量不被淘汰。实现上,需要维护一个记录数据访问次数的数组,每次访问数据,访问次数+1,数组就要重新排序,在淘汰时,只需淘汰访问次数最少的数据。LFU的命中率很高,缓存更有效,但是每次访问数据,都需要重排访问次数数据,排序消耗很大。另外,数据访问模式的经常变化,会导致缓存的性能下降。比如微博热点事件,在某个时间点上访问量突然加大,导致访问次数很大,过段时间可能很少访问,但是已经记录了很高的访问次数,导致该数据在缓存中很难被淘汰。
3、LRU
LRU:Least Recently Used,最近最少被使用,FIFO和LFU的这种方案。认为最近使用过的数据,在将来更可能被访问,尽量不被淘汰。相对于LFU中需要记录数据的访问次数,LRU只需要维护一个队列,队列头部保存刚被访问过的数据,队尾是最近最少未被访问的数据,缓存容量不够时候可以直接淘汰。
二、LRU实现
1、数据结构
- 缓存字典:LRU对象需要包含一个字典,用于缓存数据。这样根据键查找值和插入新值的复杂度都是O(1)。
- 双向链表:双向链表维护数据的最近最少使用状态。使用双向链表可以保证队尾删除节点和队头添加节点的复杂度都是O(1)
字典的键是查找值,键对应的值是双向链表对应的节点引用,这样根据字典就可以找到双向链表中的节点,进而调整双向链表中节点的顺序,更新数据的状态。
2、实现
class DLinkList:
"""定义双向链表""""
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.pre = None
self.next = None
class LRUCache:
"""LRU缓存"""
def __init__(self, capacity: int):
# 初始化容量和占用大小
self.capacity = capacity
self.size = 0
self.cache = dict()
# 初始化头结点和尾节点
self.tail = DLinkList()
self.head = DLinkList()
self.tail.pre = self.head
self.head.next = self.tail
def get(self, key: int) -> int:
# 未命中缓存
if key not in self.cache:
return -1
# 命中缓存修改将节点前移首部
node = self.cache[key]
self.moveToHead(node)
return node.value
def put(self, key: int, value: int) -> None:
# 新增缓存
if key not in self.cache:
node = DLinkList(key, value)
self.cache[key] = node
self.addToHead(node)
self.size += 1
if self.size > self.capacity:
removed_node = self.removeTail()
del self.cache[removed_node.key]
self.size -= 1
else:
# 更新缓存
node = self.cache[key]
node.value = value
self.moveToHead(node)
def removeTail(self):
# 移除尾部节点
node = self.tail.pre
self.removeNode(node)
# 这里仍旧需要将删除的节点返回,为了方便cache字典删除键值对
return node
def removeNode(self, node):
# 移除某个节点
node.next.pre = node.pre
node.pre.next = node.next
def moveToHead(self, node):
# 节点前移首部
self.removeNode(node)
self.addToHead(node)
def addToHead(self, node):
# 添加到首部
node.next = self.head.next
node.pre = self.head
self.head.next.pre = node
self.head.next = node
注:
- 字典的定义的键是查找值,键对应的值是双向链表对应节点的引用。
- 双线链表的节点保存的键值对,好处在于,淘汰尾部节点的时候可以直接从节点取出键,进而删除字典中的键值对。
- 查找数据的时候,如果缓存未命中,可以采用回调函数去查找数据库真实数据。如果命中,则返回数据的同时,仍需要将该数据对应的节点调整到链表首部,更新最近最少使用状态。
- 添加数据的时候,如果缓存容量满了,则需要淘汰链表尾部节点,也就是最近最少访问的节点。
相关链接:leetcode:lru缓存
LRU缓存及实现的更多相关文章
- LRU缓存实现(Java)
LRU Cache的LinkedHashMap实现 LRU Cache的链表+HashMap实现 LinkedHashMap的FIFO实现 调用示例 LRU是Least Recently Used 的 ...
- 转: LRU缓存介绍与实现 (Java)
引子: 我们平时总会有一个电话本记录所有朋友的电话,但是,如果有朋友经常联系,那些朋友的电话号码不用翻电话本我们也能记住,但是,如果长时间没有联系了,要再次联系那位朋友的时候,我们又不得不求助电话本, ...
- volley三种基本请求图片的方式与Lru的基本使用:正常的加载+含有Lru缓存的加载+Volley控件networkImageview的使用
首先做出全局的请求队列 package com.qg.lizhanqi.myvolleydemo; import android.app.Application; import com.android ...
- 如何用LinkedHashMap实现LRU缓存算法
阿里巴巴笔试考到了LRU,一激动忘了怎么回事了..准备不充分啊.. 缓存这个东西就是为了提高运行速度的,由于缓存是在寸土寸金的内存里面,不是在硬盘里面,所以容量是很有限的.LRU这个算法就是把最近一次 ...
- 面试挂在了 LRU 缓存算法设计上
好吧,有人可能觉得我标题党了,但我想告诉你们的是,前阵子面试确实挂在了 RLU 缓存算法的设计上了.当时做题的时候,自己想的太多了,感觉设计一个 LRU(Least recently used) 缓存 ...
- Java集合详解5:深入理解LinkedHashMap和LRU缓存
今天我们来深入探索一下LinkedHashMap的底层原理,并且使用linkedhashmap来实现LRU缓存. 摘要: HashMap和双向链表合二为一即是LinkedHashMap.所谓Linke ...
- 04 | 链表(上):如何实现LRU缓存淘汰算法?
今天我们来聊聊“链表(Linked list)”这个数据结构.学习链表有什么用呢?为了回答这个问题,我们先来讨论一个经典的链表应用场景,那就是+LRU+缓存淘汰算法. 缓存是一种提高数据读取性能的技术 ...
- LRU缓存原理
LRU(Least Recently Used) LRU是近期最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象. 采用LRU算法的缓存有两种:LrhCache和DisL ...
- 链表(上):如何实现LRU缓存淘汰算法?
一.什么是链表 和数组一样,链表也是一种线性表. 从内存结构来看,链表的内存结构是不连续的内存空间,是将一组零散的内存块串联起来,从而进行数据存储的数据结构. 链表中的每一个内存块被称为节点Node. ...
- [Leetcode]146.LRU缓存机制
Leetcode难题,题目为: 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key ...
随机推荐
- .NET对接极光消息推送
什么是APP消息推送? 很多手机APP会不定时的给用户推送消息,例如一些新闻APP会给用户推送用户可能感兴趣的新闻,或者APP有更新了,会给用户推送是否选择更新的消息等等,这就是所谓的"消息 ...
- Kong 微服务网关在 Kubernetes 的实践
来源:分布式实验室译者:qianghaohao本文主要介绍将 Kong 微服务网关作为 Kubernetes (https://www.alauda.cn)集群统一入口的最佳实践,之前写过一篇文章使用 ...
- 只需两步在Linux系统安装百度网盘--Ubuntu20
Linux Ubuntu系统安装百度网盘 百度网盘已支持Linux系统下载和使用.使用Linux系统下载并安装一个百度网盘是非常简单的,只需要以下两个步骤: 第一步 进入官网下载.deb类型的百度网盘 ...
- [BJDCTF2020]EzPHP-POP链
那次某信内部比赛中有道pop链问题的题目,我当时没有做出来,所以在此总结一下,本次以buu上复现的[MRCTF2020]Ezpop为例. 题目 1 Welcome to index.php 2 < ...
- Spring系列1:Spring基本概念
本文内容 什么是Spring? 为什么学Spring? 本系列包含哪些技术? 本系列适合哪些人? 什么是Spring? 基本概念 Spring 框架为现代基于 Java 的企业应用程序提供了一个全面的 ...
- 使用.NET 6开发TodoList应用(29)——实现静态字符串本地化功能
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 在开发一些需要支持多种语言的应用程序时,我们需要根据切换的语言来对应展示一些静态的字符串字段,在本文中我们暂时不去讨论如何结合 ...
- 我以订披萨为例,给女朋友详细讲了Java设计模式的3种工厂模式
摘要:工厂模式是将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦.从而提高项目的扩展和维护性. 本文分享自华为云社区<[Java设计模式]用 披萨订购案例 详 ...
- Javascript实现让小图片一直跟着鼠标移动
Javascript实现让小图片一直跟着鼠标移动实例 注意:图片可能加载不出来,注意更换 <!doctype html> <html> <head> <met ...
- idea 插件推荐
工欲善其事必先利其器,本文介绍几个自己在开发过程中常用的idea插件 安装方法 idea 里面在线安装 settings>plugins>marketplace 里面搜索安装 idea 官 ...
- 带你学习BFS最小步数模型
最小步数模型 一.简介 最小步数模型和最短路模型的区别? 最短路模型:某一个点到另一个点的最短距离(坐标与坐标之间) 最小步数模型:不再是点(坐标),而是状态到另一个状态的转变 BFS难点所在(最短路 ...