golang分布式缓存项目 Day 1

:该项目原作者:https://geektutu.com/post/geecache-day1.html。本文旨在记录本人做该项目时的一些疑惑解答以及部分的测试样例以便于本人复习。

LRU缓存淘汰策略

三种缓存淘汰策略

  1. FIFO(First In, First Out)先进先出
    原理:FIFO是一种最简单的缓存淘汰算法,它基于“先进先出”的原则。当缓存满了之后,最早进入缓存的数据将被首先淘汰。
    适用场景:适用于数据访问模式中,数据的访问顺序与数据进入缓存的顺序一致的场景。
    优点:实现简单,公平性较好,每个数据都有相同的淘汰机会。
    缺点:可能会淘汰掉一些频繁访问的数据,导致缓存命中率降低。
  2. LFU(Least Frequently Used)最少使用
    原理:LFU算法淘汰那些在最近一段时间内访问次数最少的数据。它的核心思想是,如果数据在过去被访问得少,那么在未来被访问的可能性也低。
    适用场景:适用于那些访问模式中,某些数据被频繁访问,而其他数据则很少被访问的场景。
    优点:可以有效地保留那些频繁访问的数据,提高缓存的命中率。
    缺点:实现相对复杂,需要跟踪每个数据的访问频率,并且在数据访问模式变化时可能需要调整策略。
  3. LRU(Least Recently Used)最近最少使用
    原理:LRU算法淘汰那些最近最少被使用的数据。它基于这样一个假设:如果数据最近被访问过,那么在未来它被访问的可能性也更高。
    适用场景:适用于那些数据访问模式中,最近访问过的数据在未来很可能再次被访问的场景。
    优点:可以有效地利用缓存空间,提高缓存命中率,是实践中最常用的缓存淘汰算法之一。
    缺点:实现相对复杂,需要维护一个数据访问的时间顺序,以便快速找到最近最少使用的数据。

LRU算法实现

算法图解

在这里插入图片描述
这张图很好地表示了 LRU 算法最核心的 2 个数据结构

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

具体代码实现

建立缓存池结构体

package projectimport ("container/list"
)type Cache struct {maxBytes  int64nbytes    int64ll        *list.Listcache     map[string]*list.ElementOnEvicted func(key string, value Value)
}type entry struct {key   stringvalue Value
}type Value interface {Len() int
}
  • 在这里我们直接使用 Go 语言标准库实现的双向链表list.List。
  • 字典的定义是 map[string]*list.Element,键是字符串,值是双向链表中对应节点的指针。
  • maxBytes 是允许使用的最大内存,nbytes 是当前已使用的内存,OnEvicted 是某条记录被移除时的回调函数,可以为nil。
  • 键值对 entry 是双向链表节点的数据类型,在链表中仍保存每个值对应的 key 的好处在于,淘汰队首节点时,需要用 key从字典中删除对应的映射。
  • 为了通用性,我们允许值是实现了 Value 接口的任意类型,该接口只包含了一个方法 Len() int,用于返回值所占用的内存大小

PS:list.Element是一个结构体其定义为

type Element struct {// Next and previous pointers in the doubly-linked list of elements.// To simplify the implementation, internally a list l is implemented// as a ring, such that &l.root is both the next element of the last// list element (l.Back()) and the previous element of the first list// element (l.Front()).next, prev *Element// The list to which this element belongs.list *List// The value stored with this element.Value any
}

所以element.Value的值可以是任何类型的,需要我们自己具体实现的时候定义下来.

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

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

查找功能

实现查找功能,具体步骤如下:

找到了
没找到
查找缓存池
是否找到
从字典中找到对应的双向链表的节点
将其放到双链表队列的队尾代表最近刚刚使用
返回空

代码实现

func (c *Cache) Get(Key string) (value Value, ok bool) {if ele, ok := c.cache[Key]; ok {c.ll.MoveToFront(ele)kv := ele.Value.(*entry)return kv.value, ok}return
}

删除功能

具体步骤如下:

找到双向链表中队首元素
更新内存池中所用的空间nbytes
删掉链表和map对应的元素
如果回调函数不空则调用

代码实现

func (c *Cache) RemoveOldest() {ele := c.ll.Back()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)}}
}

新增/修改功能

具体步骤:

存在
不存在
超出
查找缓存池中的键是否存在
更新对应节点的值并将其移到链表队尾
对应更新所用内存nbytes
判断是否超出最大内存
将其添加到缓存池和链表队尾中
对应更新所用内存nbytes
循环调用删除函数直至小于最大内存

代码实现

func (c *Cache) Add(key string, value Value) {if ele, ok := c.cache[key]; ok {c.ll.MoveToFront(ele)kv := ele.Value.(*entry)c.nbytes += int64(value.Len()) - int64(kv.value.Len())kv.value = value} else {ele := c.ll.PushFront(&entry{key, value})c.cache[key] = elec.nbytes += int64(len(key)) + int64(value.Len())}for c.maxBytes != 0 && c.maxBytes < c.nbytes {c.RemoveOldest()}
}

测试

测试所用样例及其Len()方法实现

package projectimport ("fmt""testing"
)type String stringfunc (d String) Len() int {return len(String(d))
}type test = []struct {name       stringid         intkey        stringvalue      StringexpectedOk bool
}var tests = test{{id:         1,name:       "test existed key",key:        "key1",value:      "6666",expectedOk: true,},{id:         2,name:       "test existed key",key:        "key2",value:      "1234",expectedOk: true,},{id:         3,name:       "test existed key",key:        "key3",value:      "1111",expectedOk: true,},{id:         4,name:       "test existed key",key:        "key4",value:      "1314",expectedOk: true,},
}

测试增加/修改和删除功能

代码实现

func TestGetAndRemove(t *testing.T) {lru := New(0, nil)for _, tt := range tests {lru.Add(tt.key, tt.value)if findkey, ok := lru.Get(tt.key); ok == tt.expectedOk && findkey == tt.value {fmt.Printf("find key%d successfully\n", tt.id)} else {fmt.Printf("find key%d failed\n", tt.id)}}lru.RemoveOldest()for _, tt := range tests {if findkey, ok := lru.Get(tt.key); ok == tt.expectedOk && findkey == tt.value {fmt.Printf("find key%d successfully\n", tt.id)} else {fmt.Printf("find key%d failed\n", tt.id)}}
}

测试结果
在这里插入图片描述

测试超出最大容量是能否删除最久未使用的节点

代码实现

func TestOverCapacity(t *testing.T) {lru := New(int64(24), nil)for _, tt := range tests {lru.Add(tt.key, tt.value)fmt.Printf("add key%d successfully, prsent lru usedcap = %d\n", tt.id, lru.nbytes)}for _, tt := range tests {if findkey, ok := lru.Get(tt.key); ok == tt.expectedOk && findkey == tt.value {fmt.Printf("find key%d successfully\n", tt.id)} else {fmt.Printf("find key%d failed\n", tt.id)}}
}

测试结果
在这里插入图片描述

测试回调函数

代码实现

func (c *Cache) CallBack(key string, value Value) {if _, ok := c.Get(key); ok {return} else {c.Add(key, value)c.ll.MoveToFront(c.cache[key])}
}func TestOnEvicted(t *testing.T) {LoseKeys := make([]string, 0)lru := New(int64(8), func(key string, value Value) {LoseKeys = append(LoseKeys, key)})for _, tt := range tests {lru.Add(tt.key, tt.value)fmt.Printf("add key%d successfully, prsent lru usedcap = %d\n", tt.id, lru.nbytes)}for _, tt := range LoseKeys {fmt.Printf("%s被丢弃并保存在日志中\n", tt)}
}

测试结果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/58554.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pr 视频过渡:沉浸式视频 - VR 默比乌斯缩放

效果面板/视频过渡/沉浸式视频/VR 默比乌斯缩放 Video Transitions/Immersive Video/VR Mobius Zoom VR 默比乌斯缩放 VR Mobius Zoom用于 VR 视频中的缩放式场景切换&#xff0c;通过缩小或放大的渐变效果在两个场景之间平滑过渡。 自动 VR 属性 Auto VR Properties 默认勾选…

【物联网技术】ESP8266 WIFI模块在AP模式下作为TCP服务器与多个电脑/手机网络助手(TCP客户端)通信——TCP数据透传

前言:完成ESP8266 WIFI模块在AP模式下作为TCP服务器与多个电脑/手机网络助手(TCP客户端)通信——实现TCP数据透传 AP模式,通俗来说模块可以发出一个WIFI热点提供给电脑/手机连接。 TCP服务端,通俗来说就是模块/单片机作为服务器,可以接收多个客户通道的连接。 本…

C++学习笔记----10、模块、头文件及各种主题(二)---- 预处理指令

使用#include预处理指令来包含头文件的内容。还有一些预处理指令。下面列表展示了一些常用的预处理指令&#xff1a; 预处理指令 功能 通用场景 #include [file] [file]文件名的内容插入到指令位置的代码中 几乎总是用于包含头文件以便代码可以使用定义在其它地方的功能 …

华为实时视频使用FLV播放RTSP流

import flvjs from ‘flv.js’; 安装flv <video style"width:100%;height:100%;" ref"videoHWRef" ></video>// src 华为rtsp流 rtsp://admin:Huaweivideo10.10.8.151:554/xxx/trackID1// url 需要后端提供视频源地址playVideo() {if (fl…

【系统架构设计师】高分论文:论企业应用系统的分层架构风格

更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 摘要正文摘要 2021 年 12 月,本人所在的公司承担了 “某国际贸易综合服务及经济案事件预警平台”(下文简称 “预答平台”)的升级改造工作。我担任本项目的系统架构师,负责该预答平台开发的管理、规划、设计工作…

【华为机试题】光伏场地建设规划 [Python]

题目 代码 class Solution:def func(self, input_args, area_list):count 0for i in range(input_args[0] - input_args[2] 1):for j in range(input_args[1] - input_args[2] 1):count 1 if self.area_compute(area_list,i,j,input_args[2],input_args[3]) else 0print(c…

备考25年二建,最好这样选专业!

2025年的二建备考已经开始了&#xff0c;很多考生在纠结报考哪个专业&#xff0c;二级建造师各专业难度如何&#xff1f;哪个专业含金量更高&#xff1f; 今天就带大家了解一下这六大专业&#xff0c;一起来看~ ​建筑专业 考核方向&#xff1a;建筑工程技术要求、建筑工程专…

【前端知识】es6基础语法介绍

ES6基础语法介绍 概述1. 变量声明&#xff1a;let 和 const2. 模板字符串3. 解构赋值4. 箭头函数5. 默认参数值6. 类&#xff08;Classes&#xff09;7. 模块8. 扩展运算符&#xff08;Spread Operator&#xff09;9. 对象字面量增强10. 符号&#xff08;Symbols&#xff09;11…

向量模型Jina Embedding: 从v1到v3论文笔记

文章目录 Jina Embedding: 从v1到v3Jina Embedding v1数据集准备训练过程 Jina Embedding v2预训练修改版BERT在文本对上微调在Hard Negatives上微调 Jina Embedding v2 双语言预训练修改版BERT在文本对上微调用多任务目标微调 Jina Embedding v3预训练在文本对上微调训练任务相…

「Mac畅玩鸿蒙与硬件28」UI互动应用篇5 - 滑动选择器实现

本篇将带你实现一个滑动选择器应用&#xff0c;用户可以通过滑动条选择不同的数值&#xff0c;并实时查看选定的值和提示。这是一个学习如何使用 Slider 组件、状态管理和动态文本更新的良好实践。 关键词 UI互动应用Slider 组件状态管理动态数值更新用户交互 一、功能说明 在…

全连接神经网络案例——手写数字识别

文章目录 1.我们导入需要的工具包2.数据加载3.数据处理4.模型构建5.模型编译6.模型训练7.模型测试8.模型保存 使⽤⼿写数字的MNIST数据集如上图所示&#xff0c;该数据集包含60,000个⽤于训练的样本和10,000个⽤于测试的样本&#xff0c;图像是固定⼤⼩(28x28像素)&#xff0c;…

十一:java web(3)-- Spring框架 -- Spring简介

目录 1. Servlet 与 Spring 的关系 2. Spring 框架介绍 Spring 框架的起源与发展 Spring 框架的核心特性 Spring 主要模块介绍 核心模块&#xff08;Core Container&#xff09; 数据访问与集成模块&#xff08;Data Access/Integration&#xff09; Web 模块&#xff0…

2.ARM_ARM是什么

CPU工作原理 CPU与内存中的内容&#xff1a; 内存中存放了指令&#xff0c;每一个指令存放的地址不一样&#xff0c;所需的内存空间也不一样。 运算器能够进行算数运算和逻辑运算&#xff0c;这些运算在CPU中都是以运算电路的形式存在&#xff0c;一个运算功能对应一种运算电…

MetaGeneMark:宏转录组转录本基因预测

GeneMark™ download 下载 gunzip gm_key_64.gz tar -xvzf MetaGeneMark_linux_64.tar.gz #查看安装 (完整路径)/gmhmmp #解压文件里面这个比较重要 MetaGeneMark_linux_64/mgm/MetaGeneMark_v1.mod #复制gm_key文件到主路径 mv gm_key_64 .gm_key cp .gm_key /home/zhongpei…

腾讯轻量云服务器docker拉取不到镜像的问题:拉取超时

前言 也是尝试了各种解决方案之后&#xff0c;无果&#xff0c; 后来发现每个服务器提供商都有自己的镜像加速&#xff0c;且只给自家服务器使用&#xff0c;我用的腾讯云 教程 安装docker 直接上链接&#xff1a;云服务器 搭建 Docker-实践教程-文档中心-腾讯云 配置加速镜…

各家AI性格不同,怎样取长补短

你发现了么&#xff0c;每家的AI性格也有区别呢&#xff0c;有些AI比较啰嗦&#xff0c;有些AI回答简洁明了&#xff0c;有些AI条理清晰喜欢列1、2、3。 我们在利用AI的时候&#xff0c;也要学会取长补短&#xff0c;参考各家AI的回答&#xff0c;择优录用。 例如&#xff0c…

Django安装

在终端创建django项目 1.查看自己的python版本 输入对应自己本机python的版本&#xff0c;列如我的是3.11.8 先再全局安装django依赖包 2.在控制窗口输入安装命令&#xff1a; pip3.11 install django 看到Successflully 说明我们就安装成功了 python的Scripts文件用于存…

Socket 和 WebSocket 的应用

Socket&#xff08;套接字&#xff09;是计算机网络中的一个抽象层&#xff0c;它允许应用程序通过网络进行通信。套接字用于跨网络的不同主机上的应用程序之间的数据交换。在互联网中&#xff0c;套接字通常基于 TCP&#xff08;传输控制协议&#xff09;或 UDP&#xff08;用…

Materials Studio 2023安装教程(仅作分享参考)

目录 一、软件下载 二、软件介绍 2.1 软件特点 2.2 功能模块 2.3 应用领域 三、安装步骤 一、软件下载 软件名称&#xff1a;Materials Studio 2023 软件语言&#xff1a;英文 软件大小&#xff1a;2.03G 系统要求&#xff1a;Windows10或更高&#xff0c; 64位操作系…

Spark SQL大数据分析快速上手-DataFrame应用体验

【图书介绍】《Spark SQL大数据分析快速上手》-CSDN博客 《Spark SQL大数据分析快速上手》【摘要 书评 试读】- 京东图书 大数据与数据分析_夏天又到了的博客-CSDN博客 本节主要介绍如何使用DataFrame进行编程。 4.1.1 SparkSession 在旧版本中&#xff0c;Spark SQL提供…