当前位置:首页 > 文章列表 > Golang > Go教程 > Go语言进阶freecache源码分析

Go语言进阶freecache源码分析

来源:亿速云 2023-05-08 17:14:40 0浏览 收藏

Golang小白一枚,正在不断学习积累知识,现将学习到的知识记录一下,也是将我的所得分享给大家!而今天这篇文章《Go语言进阶freecache源码分析》带大家来了解一下##content_title##,希望对大家的知识积累有所帮助,从而弥补自己的不足,助力实战开发!


这篇文章主要介绍“Go语言进阶freecache源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go语言进阶freecache源码分析”文章能帮助大家解决问题。

00. 什么是 freecache?

freecache 是一个用 go 语言实现的本地缓存系统(类似于 lru)。

它有几个特性值得注意:

  • 通过优秀的内存管理方案,实现了 go 语言的零 gc

  • 是线程安全的,同时支持一定程度的并发,非常适合并发场景

  • 支持设置失效时间,动态失效过期缓存

  • 在一定程度上支持 lru,即“最近最少使用”,会在容量不足的时候优先淘汰较早的数据

这几个优秀特性使得他非常适合用在生产环境中作为本地缓存。

01. 简单用法

cacheSize := 100 * 1024 * 1024
cache := freecache.NewCache(cacheSize)
debug.SetGCPercent(20)
key := []byte("abc")
val := []byte("def")
expire := 60 // expire in 60 seconds
cache.Set(key, val, expire)
got, err := cache.Get(key)
if err != nil {
    fmt.Println(err)
} else {
    fmt.Println(string(got))
}
affected := cache.Del(key)
fmt.Println("deleted key ", affected)
fmt.Println("entry count ", cache.EntryCount())

02. 功能概览

本文计划先以自然语言描述下列功能的实现方式,再接下来的章节中深入源码,扒出其具体实现

  • 如何做到零 gc?

  • 数据结构

  • 动态索引

  • 缓存失效

03. 如何做到 0 GC?

这个库之所以做到了 0 gc,是因为设定了很巧妙的数据结构,这个数据结构有以下的特点:

  • 无论存多少数据,都只会存在 512 个指针

  • 所有的具体数据的存储空间,都是预先就分配好的,而不是动态分配的

  • 使用了一些黑科技,比如强制类型转换以及结构体对齐等技术,将所有的动态数据都管理在预先分配好的连续空间内

04. 数据结构

Go语言进阶freecache源码分析

可以将这个数据结构大体上抽象成一个哈希表。即你会看到哈希函数以及对应的数组空间。但是他和传统的哈希表是有区别的,主要有以下几点:

  • 线程安全,支持高并发,内存空间高度优化:

  • 为了做到支持高并发以及线程安全,并且保证内存空间的可用性,freecache 实际上划分出了 256 个独立数组空间用来存储对应的底层数据。

  • 这样在并发访问的时候,通过对 key 进行分片,使得请求只会打到一个数组空间上,即只会对这 256 个空间中的一个空间加锁,这样就大大降低了资源争抢。

  • 数据的组织方式与传统哈希表不同:

  • 传统的哈希表只在数组空间中存 value。而 freecache 则不同,他将 key,value 全部存在了数组空间中。

  • 传统哈希表的数组是稀疏的。freecache 数据并不是稀疏的,而是连续的,即新的值会不断 append 到最后。

  • 传统哈希表使用 hash func 对 key 取索引,索引到稀疏数组中的位置。而 freecache 则通过维护了一个叫“slot(插槽)”的数据结构,通过对 key 进行 hash func,先拿到对应的 slot,然后 slot 中维护着一个索引,可以定位到具体的数据在数组中的位置。

  • 解决哈希冲突的方式不同。当遇到哈希冲突的时候,哈希表需要对底层的稀疏数组进行扩容,会导致可用性大大降低。而 freecache 则是只需要对“slot”的指针数组进行扩容,而无需改变底层数组。因为 slot 指针数组的大小远小于底层数组,所以扩容的成本是非常非常低的。

  • 为了实现缓存淘汰以及定时失效,它的数组空间在逻辑上是一个环状的。这么做有以下原因

  • 数组存的数据逻辑上是连续的,而非稀疏数组。充分利用了CPU对数组读取的缓存优化

  • 通过使用了一系列的首尾计算方式,是足以保证读取和存储在首尾的连续性。比如读数据的时候读到结尾如果还没读完,会跳转到头部继续往下读。

  • 能以 O(1) 的时间复杂度完成新数据对旧数据的淘汰。我们假设如果数组在逻辑上不是环形的,那么当数组写满的时候再写入新的数据,就需要把数组头部的数据删除掉,然后再把之后的数据统统向左移动删除数据的长度,然后再从最右端写入新的数据。反之,如果数组是环形的,只需要在数组头部把旧数据覆盖即可

  • 通过一些算法手段,可以实现一个很 hack 的 lru 功能。在一定程度上能保证“最近最少使用”的淘汰。

05. 动态索引

通过上面对数据结构的分析,我们知道他和传统的哈希表的实现方式大相径庭。它实际上是使用了一种叫“插槽”的数据结构,专门负责通过 key 索引到数据空间中的某个位置。他的实现比较有意思。它的底层是一个结构体指针数组。他是可以动态扩容的。每个插槽有一个 id,叫 slotId,范围是 0~255。当遇到哈希冲突的时候,比如出现了2个 slotId 为 100 的 slot,他就会检测当前的最大重复 slotID 数量 n。如果这个 2 大于 n,他就会扩容到 2n。

// slot 的数据结构
type entryPtr struct {
	offset   int64  // 记录了当前 slot 在环形数组中的偏移量
	hash26   uint16 // 一个 hash 值,用于在 segment 中定位到具体的 slot
	keyLen   uint16 // used to compare a key
	reserved uint32
}
// 每个分片的数据结构
type segment struct {
	rb            RingBuf // 环形数组
	segId         int
	hitCount      int64
	missCount     int64
	entryCount    int64
	totalCount    int64      // 之后计算 lru 的时候会用到,用于衡量一个数据是否是热点数据
	totalTime     int64      // 之后计算 lru 的时候会用到,用于衡量一个数据是否是热点数据
	totalEvacuate int64      // used for debug
	totalExpired  int64      // used for debug
	overwrites    int64      // used for debug
	vacuumLen     int64      // 环形数组可用容量,主要用于环形数组首尾相接的算法
	slotLens      [256]int32 // 每个 slotId 的长度的数组
	slotCap       int32      // 每个 slotId 的容量
	slotsData     []entryPtr // 存储 slots 的切片,根据 hash26 进行顺序排列。相同的 hash26 拥有相同的 slotId
}

06. 缓存失效

缓存失效主要包括两种:

  • 基于过期时间的失效

  • 基于最近最少使用的失效

这两种失效都有缺陷。

首先它是一种被动失效,而不是通过一个额外的线程定期check。而是每次 set 新的值的时候,如果发现空间不够用了,他才会尝试从环形数组的头端进行check。如果发现当前check的数据过期了,或者使用频率过低,就会将记录有效数据的头指针进行偏移,即相当于“失效”。如果检查多次都没能找到需要失效的数据,那么他会将这些检查过的数据转移到尾部,并强制当前的头部的数据失效。

这种失效是不可靠的。比如一个数据,如果在环形数组的中间,那么即便它过期了,也很难被 check 到。并且存在一定的失误概率,即将一个热点数据给失效了。

关于“Go语言进阶freecache源码分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注golang学习网行业资讯频道,小编每天都会为大家更新不同的知识点。

到这里,我们也就讲完了《Go语言进阶freecache源码分析》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于golang的知识点!

版本声明
本文转载于:亿速云 如有侵犯,请联系study_golang@163.com删除
Go语言标准库strconv怎么使用Go语言标准库strconv怎么使用
上一篇
Go语言标准库strconv怎么使用
常见的基于Redis缓存数据问题及解决方案
下一篇
常见的基于Redis缓存数据问题及解决方案
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    508次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    497次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • 笔灵AI生成答辩PPT:高效制作学术与职场PPT的利器
    笔灵AI生成答辩PPT
    探索笔灵AI生成答辩PPT的强大功能,快速制作高质量答辩PPT。精准内容提取、多样模板匹配、数据可视化、配套自述稿生成,让您的学术和职场展示更加专业与高效。
    23次使用
  • 知网AIGC检测服务系统:精准识别学术文本中的AI生成内容
    知网AIGC检测服务系统
    知网AIGC检测服务系统,专注于检测学术文本中的疑似AI生成内容。依托知网海量高质量文献资源,结合先进的“知识增强AIGC检测技术”,系统能够从语言模式和语义逻辑两方面精准识别AI生成内容,适用于学术研究、教育和企业领域,确保文本的真实性和原创性。
    35次使用
  • AIGC检测服务:AIbiye助力确保论文原创性
    AIGC检测-Aibiye
    AIbiye官网推出的AIGC检测服务,专注于检测ChatGPT、Gemini、Claude等AIGC工具生成的文本,帮助用户确保论文的原创性和学术规范。支持txt和doc(x)格式,检测范围为论文正文,提供高准确性和便捷的用户体验。
    37次使用
  • 易笔AI论文平台:快速生成高质量学术论文的利器
    易笔AI论文
    易笔AI论文平台提供自动写作、格式校对、查重检测等功能,支持多种学术领域的论文生成。价格优惠,界面友好,操作简便,适用于学术研究者、学生及论文辅导机构。
    47次使用
  • 笔启AI论文写作平台:多类型论文生成与多语言支持
    笔启AI论文写作平台
    笔启AI论文写作平台提供多类型论文生成服务,支持多语言写作,满足学术研究者、学生和职场人士的需求。平台采用AI 4.0版本,确保论文质量和原创性,并提供查重保障和隐私保护。
    40次使用
查看更多
相关文章
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码