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. 使用Spring Boot创建docker image

    目录 简介 传统做法和它的缺点 使用Buildpacks Layered Jars 自定义Layer 简介 在很久很久以前,我们是怎么创建Spring Boot的docker image呢?最最通用的 ...

  2. 【C/C++编程入门学习】同样是数据类型,链表对比数组?哪一个更香?

    说起链表,第一反应:链表是一种数据类型!它可以用来存储同种类型多个批量数据.   有了这种认知,很容易去联想到数组,它也是一种数据类型,也可以用来存储同种类型的批量数据.初学者往往对数组的印象比较好, ...

  3. 如果你想or即将成为一名程序员,那你需要知道这些东西!上岗须知~

    前两天公司学院的同学给我看了一下即将入职的应届生的数量,真是不少.感慨一下,一批新人即将到来,而自己又老去了一岁.码农是一个必将终身学习的职业.而相关的知识越来越多了.接下来该学什么?接下来该干什么? ...

  4. JVM系列【6】GC与调优5-日志分析

    JVM系列笔记目录 虚拟机的基础概念 class文件结构 class文件加载过程 jvm内存模型 JVM常用指令 GC与调优 主要内容 分析PS.CMS.G1的回收日志,目标使大概能读懂GC日志. 测 ...

  5. centos8平台安装zookeeper3.6集群

    一,规划三台zk服务器构成集群 ip:172.18.1.1 机器名:zk1 对应myid: 1 ip:172.18.1.2 机器名:zk2 对应myid: 2 ip:172.18.1.3 机器名:zk ...

  6. laravel job 队列

    1.数据库建表 php artisan queue:table<span> </span>//队列任务表 php artisan queue:failed-table<s ...

  7. eShopOnContainers 知多少[12]:Envoy gateways

    1. 引言 在最新的eShopOnContainers 3.0 中Ocelot 网关被Envoy Proxy 替换.下面就来简要带大家了解下Envoy,并尝试梳理下为什么要使用Envoy替代Ocelo ...

  8. 解析SparkStreaming和Kafka集成的两种方式

    spark streaming是基于微批处理的流式计算引擎,通常是利用spark core或者spark core与spark sql一起来处理数据.在企业实时处理架构中,通常将spark strea ...

  9. Hadoop 指令

    date: 2018-04-30 09:07:56 updated: 2018-04-30 09:07:56 1.ls hadoop fs -ls / 列出hdfs文件系统根目录下的目录和文件 had ...

  10. 通过Relect反射方法创建对象,获得对象的方法,输出对象信息

    package reflects; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java. ...