iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > GO >Go语言实现LRU算法的核心思想和实现过程
  • 529
分享到

Go语言实现LRU算法的核心思想和实现过程

Go LRU算法GoLang LRU算法 2023-05-20 08:05:54 529人浏览 薄情痞子
摘要

目录Go实现Redis的LRU例子1.FIFO/LFU/LRU算法简介2.LRU算法实现2.1核心数据结构2.2查找功能2.3删除2.4新增或修改GO实现Redis的LRU例子 常见

GO实现Redis的LRU例子

常见的三种缓存淘汰算法有三种:FIFO,LRU和LFU

实现LRU缓存淘汰算法

1.FIFO/LFU/LRU算法简介

缓存全部存在内存中,内存是有限的,因此不可能无限制的添加数据,假定我们能够最大使用的内存为Max,那么在一个时间点之后,添加了某一条缓存记录之后,占用内存超过了N,这个时候就需要从缓存中移除一些数据,那么该移除或者淘汰谁呢?我们肯定希望去移除没用的数据,将热点数据留在缓存中,下面几种就是对应的几种策略!

FIFO (First in First Out)

先进先出,内存满了时淘汰最老存在最久的key缓存,这种算法认为越早被添加的数据使用可能性会比新加入进来的小,但是这种也有弊端,在某些场景下比如查历史支付记录的账单,就需要查询之前的缓存记录,这类记录就不得不因为每天新的支付从而淘汰掉以前的支付记录(当然这类记录存库是最好方式)

【实现方式】维护一个队列先进先出就行了,新来的数据加到队尾

LFU (Least Frequently Used)

最少使用,也就是淘汰缓存中访问频率最低的记录。这个算法认为过去访问次数最高的使用概率也最大,但是这样就额外维护了一个访问次数,对内存其实占用也挺多的,再者访问次数最多的数据,突然不使用了,那么要淘汰它之前,需要内存其他区域的数据访问次数全部超过它才有可能,所以淘汰的缺点也非常明显。

【实现方式】LFU 的实现需要维护一个按照访问次数排序的队列,每次访问,访问次数加1,队列重新排序,淘汰时选择访问次数最少的即可

LRU (Least Recently Used)

最近最少使用,相对于只考虑使用时间和使用次数来看,LRU会相对比较平均去淘汰数据,如果数据最近被使用过,那么将来被访问的概率将提高

【实现方式】维护一个队列,如果某条记录被访问了,则移动到队尾,那么队首则是最近最少访问的数据,淘汰该条记录即可。

2.LRU算法实现

2.1核心数据结构

这张图很好地表示了 LRU 算法最核心的 2 个数据结构

  • 绿色的是字典(map),存储键和值的映射关系。这样根据某个键(key)查找对应的值(value)的复杂是O(1),在字典中插入一条记录的复杂度也是O(1)
  • 红色的是双向链表(double linked list)实现的队列。将所有的值放到双向链表中,这样,当访问到某个值时,将其移动到队尾的复杂度是O(1),在队尾新增一条记录以及删除一条记录的复杂度均为O(1)

接下来我们创建一个包含字典和双向链表的结构体类型 Cache,方便实现后续的增删查改操作。

package lru
import (
	"container/list"
	"log"
)
// Cache is a LRU cache. It is not safe for concurrent access.
type Cache struct {
	maxBytes int64 // 允许使用的最大内存
	nbytes   int64 // 当前已使用的内存
	ll       *list.List
	cache    map[string]*list.Element
	// optional and executed when an entry is purged.
	OnEvicted func(key string, value Value) // 某条记录被移除时的回调函数
}
type entry struct {
	key   string
	value Value
}
// Value use Len to count how many bytes it takes
type Value interface {
	Len() int
}
  • 在这里我们直接使用 Go 语言标准库实现的双向链表list.List
  • 字典的定义是 map[string]*list.Element,键是字符串,值是双向链表中对应节点的指针。
  • maxBytes 是允许使用的最大内存,nbytes 是当前已使用的内存,OnEvicted 是某条记录被移除时的回调函数,可以为 nil。
  • 键值对 entry 是双向链表节点的数据类型,在链表中仍保存每个值对应的 key 的好处在于,淘汰队首节点时,需要用 key 从字典中删除对应的映射。
  • 为了通用性,我们允许值是实现了 Value 接口的任意类型,该接口只包含了一个方法 Len() int,用于返回值所占用的内存大小。

方便实例化 Cache,实现 New() 函数:

// New is the Constructor of Cache
func New(maxBytes int64, onEvicted func(string, Value)) *Cache {
	return &Cache{
		maxBytes:  maxBytes,
		ll:        list.New(),
		cache:     make(map[string]*list.Element),
		OnEvicted: onEvicted,
	}
}

2.2查找功能

查找主要有 2 个步骤,第一步是从字典中找到对应的双向链表的节点,第二步,将该节点移动到队尾。

func (c *Cache)Get(key string)(val Value,ok bool){
	// 如果找到了节点,就将缓存移动到队尾
	if ele,ok:=c.cache[key];ok{
		c.ll.MoveToBack(ele)
		kv:=ele.Value.(*entry)
		return kv.value,true
	}
	return
}
  • 如果键对应的链表节点存在,则将对应节点移动到队尾,并返回查找到的值。
  • c.ll.MoveToBack(ele),即将链表中的节点 ele 移动到队尾

2.3删除

这里的删除,实际上是缓存淘汰。即移除最近最少访问的节点(队首)

// 移除最近最近,最少访问的的节点
func (c *Cache)RemoveOldest(){
	ele:=c.ll.Front()  // 取到队首节点,从链表中删除
	if ele!=nil{
		c.ll.Remove(ele)
	    kv:=ele.Value.(*entry)
	    delete(c.cache,kv.key)
	    c.nbytes-=int64(len(kv.key))+int64(kv.value.Len())
		if c.OnEvicted != nil {
			c.OnEvicted(kv.key, kv.value)
		}
	}
}
  • c.ll.Front() 取到队首节点,从链表中删除。
  • delete(c.cache, kv.key),从字典中 c.cache 删除该节点的映射关系。
  • 更新当前所用的内存 c.nbytes
  • 如果回调函数 OnEvicted 不为 nil,则调用回调函数

2.4新增或修改

// 新增或修改
func (c *Cache)Add(key string ,value Value){
   if int64(value.Len())>c.maxBytes{
      log.Printf("超过最大容量%d,插入缓存容量为%d",c.maxBytes,int64(value.Len()))
      return
   }
   if ele,ok:=c.cache[key];ok{
          c.ll.MoveToBack(ele)
          kv:=ele.Value.(*entry)
       c.nbytes += int64(value.Len()) - int64(kv.value.Len())
   }else{
      ele:=c.ll.PushFront(&entry{key: key,value: value})
      c.cache[key]=ele
      c.nbytes=int64(len(key))+int64(value.Len())
   }
   // 如果当前使用的内存>允许使用的最大内存 使用淘汰策略
   for c.maxBytes !=0 && c.maxBytes < c.nbytes{
       c.RemoveOldest()
   }
}
  • 如果键存在,则更新对应节点的值,并将该节点移到队尾。
  • 不存在则是新增场景,首先队尾添加新节点 &entry{key, value}, 并字典中添加 key 和节点的映射关系。
  • 更新 c.nbytes,如果超过了设定的最大值 c.maxBytes,则移除最少访问的节点。

到此这篇关于Go语言实现LRU算法的核心思想和实现过程的文章就介绍到这了,更多相关Go LRU算法内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文档:

--结束END--

本文标题: Go语言实现LRU算法的核心思想和实现过程

本文链接: https://www.lsjlt.com/news/214206.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Go语言实现LRU算法的核心思想和实现过程
    目录GO实现Redis的LRU例子1.FIFO/LFU/LRU算法简介2.LRU算法实现2.1核心数据结构2.2查找功能2.3删除2.4新增或修改GO实现Redis的LRU例子 常见...
    99+
    2023-05-20
    Go LRU算法 GoLang LRU算法
  • Go语言如何实现LRU算法的核心思想和实现过程
    这篇文章主要介绍了Go语言如何实现LRU算法的核心思想和实现过程,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。GO实现Redis的LRU例子常见的三种缓存淘汰算法有三种:FIFO,LRU和LFU实现LRU缓存淘汰算法1....
    99+
    2023-07-06
  • Go语言使用组合的思想实现继承
    目录前言类型嵌入结构体类型嵌入接口类型嵌入小结前言 Go 语言的设计之初,就不打算支持面向对象的编程特性,因此 Go 不支持面向对象的三大特性之一——继承。但...
    99+
    2022-12-16
    Go语言继承 Go 继承
  • C语言实现页面置换算法(FIFO、LRU)
    目录1.实现效果2.实现源代码 1.实现效果 2.实现源代码  #include<iostream> #include<process.h> #inc...
    99+
    2024-04-02
  • Go语言编程中,如何实现高效的算法思维?
    Go语言作为一种高性能的编程语言,其在算法领域也有着广泛的应用。然而,如何实现高效的算法思维是一个需要探讨的问题。在本文中,我们将介绍一些在Go语言编程中实现高效算法思维的技巧和方法。 一、数据结构 数据结构是实现高效算法思维的基础。在Go...
    99+
    2023-08-08
    编程算法 bash 响应
  • Go语言实现Snowflake雪花算法
    目录介绍 雪花算法 UUID 数据库自增主键Redis Snowflake 实现原理 代码实现 实现步骤 代码实现 每次放长假的在家里的时候,总想找点简单的例子来看看实现原理,这次我...
    99+
    2024-04-02
  • GO语言的编程算法:如何在UNIX和Linux上实现?
    随着计算机技术的发展,编程语言也在不断地更新和发展。其中,GO语言是一种新兴的编程语言,它具有高效、简洁、安全等特点,越来越受到广大程序员的关注和喜爱。在UNIX和Linux操作系统上,GO语言的编程算法也是非常实用的,本文将介绍如何在U...
    99+
    2023-06-09
    unix linux 编程算法
  • Go语言如何实现实时函数编程算法?
    随着云计算、大数据、物联网等技术的发展,实时数据处理的需求越来越迫切。实时函数编程算法能够帮助我们处理实时数据,帮助我们更好地理解和应用这些数据。那么,Go语言如何实现实时函数编程算法呢?本文将为大家介绍。 一、Go语言实现实时函数编程算...
    99+
    2023-07-04
    实时 函数 编程算法
  • React实现核心Diff算法的示例代码
    目录Diff算法的设计思路Demo介绍Diff算法实现遍历前的准备工作遍历after遍历后的收尾工作总结Diff算法的设计思路 试想,Diff算法需要考虑多少种情况呢?大体分三种,分...
    99+
    2024-04-02
  • Go语言编程中,如何实现高效的算法?
    随着计算机技术的不断发展,算法也成为了计算机领域中的重要话题。在Go语言编程中,如何实现高效的算法呢?本文将为大家介绍一些实现高效算法的方法和技巧。 一、使用适当的数据结构 在Go语言编程中,使用适当的数据结构可以大大提高算法的效率。比如...
    99+
    2023-08-08
    编程算法 bash 响应
  • go语言心跳超时的实现示例
    目录一、背景二、心跳超时的实现2.1 通过select case (设计概念比较多)2.2 通过time.sleep(简单有效)三、个人的实现观感一、背景 本文描述的是客户端接收心跳...
    99+
    2024-04-02
  • Go 语言编程算法:如何在 LeetCode 中实现?
    LeetCode 是一款非常受欢迎的在线算法编程平台,拥有大量的算法题目和编程挑战,让程序员们可以在这里锻炼算法能力和编程技巧。而 Go 语言作为一种高效、简洁、安全的编程语言,也越来越受到程序员们的青睐。在本文中,我们将介绍如何在 Le...
    99+
    2023-08-20
    leetcode javascript 编程算法
  • 熟悉 Go 语言中的算法和数据结构实现
    在当今互联网时代,编程语言的选择显得尤为重要。Go 语言作为 Google 开发的一门编程语言,早已在互联网行业中占据了重要的地位。在 Go 语言中,算法和数据结构是一个非常重要的方面...
    99+
    2024-04-02
  • Go 语言简单实现Vigenere加密算法
    目录Vigenere 加密算法Go 代码Vigenere 加密算法 该密码由意大利密码学家 Giovan Battista Bellaso 于 1553 年发明,但几个世纪以来一直归...
    99+
    2024-04-02
  • Go语言基础教程:四则运算的实现方法
    Go语言基础教程:四则运算的实现方法,需要具体代码示例引言:Go语言作为一门开发云原生应用的编程语言,受到越来越多开发者的青睐。作为学习Go语言的初学者,掌握基本的运算操作是必不可少的。本文将介绍Go语言下实现四则运算的基本方法,并提供具体...
    99+
    2023-12-23
    Go语言 基础教程 四则运算
  • Go语言编程中的实时函数算法是如何实现的?
    实时函数算法是一种高效的算法,可以用于处理实时数据。在Go语言编程中,实时函数算法实现起来非常简单,只需要使用一些基本的数据结构和算法即可。 实时函数算法的核心思想是将数据流式处理,每当有新的数据到来时,就立即计算出新的结果。这种算法非常...
    99+
    2023-07-04
    实时 函数 编程算法
  • Go语言怎么实现Snowflake雪花算法
    这篇文章主要介绍了Go语言怎么实现Snowflake雪花算法,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。雪花算法雪花算法的原始版本是scala版,用于生成分布式ID(纯数字...
    99+
    2023-06-15
  • 编程算法:Go语言中如何实现最优解?
    Go语言作为一种高效、简洁、并发的编程语言,已经成为了业内广泛使用的语言之一。而在编写程序时,算法的选择和实现方式往往会对程序的运行效率产生巨大的影响。本文将介绍在Go语言中如何实现最优解的算法,以及如何通过优化算法来提高程序的性能。 一...
    99+
    2023-07-04
    异步编程 编程算法 函数
  • 用Go语言实现你的创意项目:让梦想成为现实
    让创意成真:用Go语言开发你的梦想项目 导言:在现今数字化时代,创意无处不在。无论是开发一个新的应用程序、设计一个创新的网站,还是构建一个智能机器人,每个人都可以迈出一步,将自己的梦想转化为现实。而Go语言作...
    99+
    2024-01-23
    创意 Go语言 梦想项目
  • 如何在Go语言中实现常用的算法?
    Go语言是一种快速、简单、可靠的编程语言,它已经成为了云计算、网络编程、大数据等领域的热门语言。在软件开发过程中,算法是一个不可或缺的部分。本文将介绍如何在Go语言中实现常用的算法。 一、排序算法 1.冒泡排序 冒泡排序是一种简单的排序算法...
    99+
    2023-06-17
    教程 编程算法 numy
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作