LRU

Redis的内存淘汰机制好几种,如ttl、random、lru。

lru(less recently used)即最近最少使用策略,表示在最近一段时间内最少被使用到的Redis键,如果遇到内存不足,会有限淘汰这部分键来腾出更多空间。

今天就来说说lru这种淘汰策略是如何通过链表这种结构实现的。

难点

在链表结构中,如何表示最近访问的节点和如何表示最久没有访问的节点?

如何判定一个链表中是否存在要查找的节点?

如何向链表结构插入节点,并放在最近最新的节点位置?

如果在链表中删除一个节点?

思路

结合上面的难点,我们可以构建一个可以解决问题的链表模型。

如何表示最近访问的节点和如何表示最久没有访问的节点

可以设计一个双向链表,头结点表示最近访问的节点,尾结点表示最久没有访问的节点。使用双向链表是为了查找和定位更加方便。

如何判定一个链表中是否存在要查找的节点

解决这个问题,最直接的思路就是遍历整个链表,依次匹配如果找到相同的值,对应的节点就是待查找的节点,如果遍历完整个链表,还是没有找到,表示该链表不存在该节点。

还有一种思路是将链表的所有节点存放到一个map结合中,查找的时候直接通过map的key进行查找即可。

如何向链表结构插入节点,并放在最近最新的节点位置

结合前面几篇,我们知道,链表的插入和删除是非常方便的,但是在lru问题背景下,如果插入节点并保证是最新的位置呢?显然最新的节点是要放到头结点的。

另外需要注意的点是,插入之前需要先查找这个节点是否存在链表中,如果存在需要先删除。

如果在链表中删除一个节点

删除一个节点的前置步骤应该是先判定一个节点是否存在链表中,如果存在删除即可,如果不存在则无需删除。

通过以上几个问题,我们大概可以构想出几个原子函数

  • 初始化双向链表结构
  • 查找指定节点
  • 插入指定节点
  • 删除指定节点

下面我们主要看如何实现这几个函数就可以了,主要代码如下

type LRUCache struct {
Cap int
Map map[int]*Node
Head *Node
Last *Node
} type Node struct {
Val int
Key int
Pre *Node
Next *Node
} func Constructor(capacity int) LRUCache {
cache := LRUCache{
Cap: capacity,
Map: make(map[int]*Node, capacity),
Head: &Node{},
Last: &Node{},
}
cache.Head.Next = cache.Last
cache.Last.Pre = cache.Head
return cache
} func (this *LRUCache) Get(key int) int {
node, ok := this.Map[key]
if !ok {
return -1
}
this.remove(node)
this.setHeader(node)
return node.Val
} func (this *LRUCache) Put(key int, value int) {
node, ok := this.Map[key]
if ok {
this.remove(node)
} else {
if len(this.Map) == this.Cap {
delete(this.Map, this.Last.Pre.Key)
this.remove(this.Last.Pre)
}
node = &Node{Val: value, Key: key}
this.Map[node.Key] = node
}
node.Val = value
this.setHeader(node)
} func (this *LRUCache) setHeader(node *Node) {
this.Head.Next.Pre = node
node.Next = this.Head.Next
this.Head.Next = node
node.Pre = this.Head
} func (this *LRUCache) remove(node *Node) {
node.Pre.Next = node.Next
node.Next.Pre = node.Pre
}

  

初始化的双向链表如上图所示,一个节点包括数据部分data,前继节点pre和后继节点next。

所有节点数据放入map集合中。

Get()方法会在map中查找,如果不存在,则直接返回。如果存在,则调用remove先删除该节点,再调用setHeader将节点放入头结点。

Put()方法会首先在map中查找对应节点,如果找到,则先调用remove删除方法删除改节点,并调用setHeader方法将节点放入头结点。

至此一个lru的淘汰策略使用一个双向链表就实现了。

链表总结

前面几篇,分别介绍了通过链表结构如何实现链表反转、判断链表是否有环、链表结构的回文判断、有序链表的合并以及本篇的lru实现。

链表具备插入删除方便,但是查找效率较低的特性。

以下是对于链表结构的梳理

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

【日拱一卒】链表——如何实现lru的更多相关文章

  1. 别做操之过急的”无效将军”,做实实在在的”日拱一卒” zz

    别做操之过急的”无效将军”,做实实在在的”日拱一卒” 前天在网上看到一句话很不错,拿来和大家分享,同时用我的“大叔”三观来解读这句话. 这句话是:“我们不需要操之过急的”无效将军”,我们需要实实在在的 ...

  2. Windows Server 2008 网管数据采集 努力做“日拱一卒“

    Windows Server 2008R2系统管理[刘道军主讲MCITP课程] http://edu.51cto.com/course/course_id-510.html Windows Serve ...

  3. lintcode刷题笔记(一)

    最近开始刷lintcode,记录下自己的答案,数字即为lintcode题目号,语言为python3,坚持日拱一卒吧... (一). 回文字符窜问题(Palindrome problem) 627. L ...

  4. 比较两个文件不同以及生成SQL插入语句

    Tips 做一个终身学习的人! 日拱一卒,功不唐捐. 今天有个小小的需求,具体需求是这样的: 有两个文本文件,每个文件都有一些字符串文本: 比较第一个文件中,在第二个文件中,不存在的字符串文本: 把这 ...

  5. 1. Apache ZooKeeper快速课程入门

    Tips Tips做一个终身学习的人! 日拱一卒,功不唐捐. 在过去的几十年里,互联网改变了我们生活的方式.Internet上提供的服务通常由复杂的软件系统支持,这些系统跨越了大量的服务器,而且常常位 ...

  6. 2.动手实操Apache ZooKeeper

    Tips 做一个终身学习的人! 日拱一卒,功不唐捐. 在本节中,我们将讲解如何下载并安装Apache ZooKeeper,以便我们可以直接开始使用ZooKeeper. 本部分旨在通过提供详细的安装和使 ...

  7. 我的$OI$

    我只是懒得写日记啦\(\color{pink}{qwq}\) //11月8日 啊--终于要\(NOIp\)了,为此期盼了好久.紧张了好久的我,不知道会迎来怎样的结果. 我只知道这段回忆是值得保留封存的 ...

  8. NSDate 时间加减

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/pearlhuzhu/article/details/26227393 NSDate有个类别,例如以下 ...

  9. Atitit.js this错误指向window的解决方案

    Atitit.js this错误指向window的解决方案 1.1. 出现地点and解决之道1 1.2. call,apply和bind这三个方法2 1.2.1. Function.prototype ...

随机推荐

  1. MeteoInfoLab脚本示例:subplot

    subplot命令可以自动等间距分配多个坐标系(Axes),命令中有三个参数,前两个定义了行数和列数,第三个指定了当前的坐标系(Axes),绘图命令是作用在当前坐标系中的. 脚本程序: def f(t ...

  2. Socket编程,C语言版!

    socket编程--send函数&recv函数详解 一.send函数 ✍ 函数原型: int send( SOCKET s,char *buf,int len,int flags ); ✍ 功 ...

  3. 【树形DP】NOI2003 逃学的小孩

    题目大意 题目链接 PS:可能出题人为了提高难度故意加了很多废话--实际上题目是很简单的 在一棵树上找3个点A.B.C,使AB+BC最大,且满足AC>AB. 样例输入 4 31 2 12 3 1 ...

  4. BUUCTF-[极客大挑战 2019]HardSQL 1详解

    来到sql注入骚姿势,我们一点一点开始学 我们来到这道题,然后尝试注入,结果发现 拼接'or '1'='1 'or '1'='2如果是字符型注入则会报错,然而并没有而是显示的页面一样, 通过常规注入, ...

  5. composer 打印美化

    { "name": "brady_frmwork", "description":"php framwork", &qu ...

  6. spring boot:用redis+lua限制短信验证码的发送频率(spring boot 2.3.2)

    一,为什么要限制短信验证码的发送频率? 1,短信验证码每条短信都有成本制约, 肯定不能被刷接口的乱发 而且接口被刷会影响到用户的体验, 影响服务端的正常访问, 所以既使有图形验证码等的保护, 我们仍然 ...

  7. 通过jQuery来获取DropDownList的Text/Value属性值

    脚本代码: <script src="Scripts/jquery-1.4.1-vsdoc.js" type="text/javascript">& ...

  8. 5年Android程序员面试字节跳动两轮后被完虐,请查收给你的面试指南

    大家应该看过很多分享面试成功的经验,但根据幸存者偏差的理论,也许多看看别人面试失败在哪里,对自己才更有帮助. 最近跟一个朋友聊天,他准备了几个月,刚刚参加完字节跳动面试,第二面结束后,嗯,挂了- 所以 ...

  9. OpenCV412+contrib+CUDA+cuDNN

    首先现在好软件(包含源码和官方编译好的库)和contrib源码包(对应版本怎么下载????) 软件: contirb包: 1.先把软件解压,在目录新建mybuild文件夹,用于输出保存自定义VS工程 ...

  10. 专攻知识小点——回顾JavaWeb中的servlet(三)

    HttpSession基本概述 ** ** 1.HttpSession:是服务器端的技术.和Cookie一样也是服务器和客户端的会话.获得该对象是通过HTTPServletRequest的方法getS ...