golang本地缓存库之bigcache

2024-04-22 19:53
文章标签 golang 本地 缓存 bigcache

本文主要是介绍golang本地缓存库之bigcache,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 前言

上周工作之余逛github看到一个本地缓存库bigcache,这个是allegro公司开源的一个项目,主要是用于本地缓存使用,根据他们的博客说明,他们编写这个库最初的目的就是实现一个非常快速的缓存服务。

看了下bigcache这个库的源码,这个库主要在两个方面进行了一定的创新:

  1. 并发性
    1. 缓存获取的时候会产生并发,会涉及锁的争用问题,bigcache通过分片的机制解决了并发的问题
  2. 省略垃圾收集器
    1. Go中的垃圾收集器在GC期间会在扫描和标记阶段去访问map中的每一个键值对,当map很大的时候会造成系统性能的下降。在go中如果你使用的map的键值都不包含任何指针元素的话,则GC会忽略这些内容,所以bigcache通过将所有的键都定义成整数类型,从而避免了GC。

2. 为什么不使用Redis等缓存中间件呢?

关于为什么不使用Redis/Memcached这类缓存,这是因为这些缓存中间件都属于外部依赖,在对缓存进行操作时都存在网络上的延迟,所以就生出了bigcache。

3. 并发分片

缓存服务会同时接收多个请求,因此需要对缓存提供并发访问,而实现并发访问的简单方法是利用读写锁,即sync.RWMutex来实现,从而确保每一次只有一个goroutine可以修改缓存,但这样会阻塞其他的Goroutine,从而触及性能瓶颈。
在这里插入图片描述

为了解决这个问题,bigcache通过分片的机制,即将cache分成N个shard,即每个shard存储一部分数据,而在对Key的数据进行获取或者存储的时候,通过hash(key)%N 即可实现分片位置的获取。在N足够大的时候,锁的争用可以最小化到几乎为0。
在这里插入图片描述

3.过期键值的驱逐

本地缓存肯定有缓存大小的上限,为了避免本地缓存无限增大的问题,就需要驱逐过期的键值对,bigcache通过一个fifo的队列(在bigcache中,这个fifo的实现是通过BytesQueue来实现的,即每个键值的插入与删除都是通过具体的偏移量来实现的,驱逐则是通过移动fifo的头类实现的),来实现数据驱逐的问题。

当缓存的键值对数据被添加到缓存中的时候,会发生两个额外的操作:

  1. 包含key和创建时间的键值对会被添加到队列的尾部
  2. 从队列中读取最旧的元素,将元素的创建时间戳与当前时间进行比较,如果超出了存活的生命周期大小,则队列中的元素键值对将会被删除。

由于键值的驱逐是需要获取锁来实现的,所以一般都是在写入缓存的期间执行驱逐行为。

4. 省略垃圾收集器

在Go中,如果你有一个map,则垃圾收集器会在标记和扫描阶段访问该map的每一个项目,当map足够大的时候,可能会对应用程序的性能造成较大的影响。

因为bigcache的定位就是满足数百万个键值对的存储,所以会使得GC耗费的时间变长,从而使得应用程序性能下降。

GC只在堆上发生,可以考虑去堆外存储这些元素,但如果去堆外存储,则需要依赖额外的库(offheap)。

另一种是通过减少指针的数量来实现零GC开销的map,可以参考freecache,将键值保存在环形缓冲区中,使用索引切片查找条目。

最后一种就是bigcache使用的基于go的一个优化方法,优化表明如果map中的键值没有使用指针类型,则GC会忽略这些内容。但Go中所有的数据基本都是基于指针构建的,比如结构体、切片以及数组等。这就意味着我们需要把这些键值修改为ini或者bool这样的数据,从而避免键值设置为指针。

在上面并发分片中,采用分片的方式存储多组缓存数据从而增加并发度,这里bigcache会复用分片的理论,因为分片是将key通过hash方法变成一个int类型的key,而通过int类型的key我们可以找到具体的分片位置,具体的缓存是存储在分片中的,而分片缓存中的map是map[int][int]类型的,则key表示我们获取的hashkey,值则表示具体的数据的偏移量。这样在获取某个key数据的时候,我们只需要:

  1. 通过hash函数获取hashedKey
  2. 通过hashedKey获取分片
  3. 通过分片中hashedKey的偏移量
  4. 通过偏移量获取数据

在这里插入图片描述

5. 总结

bigcache用于在本地存储数以百万计的键值对拥有比较好的性能,这个主要得意于其采用的:

  1. 并发分片
  2. 零GC

并发分片的方式帮助bigcache在分片足够大的时候,可以做到goroutine争用为0,而通过Go官方对map键值非指针的情况下,GC会忽略这些内容的优化,bigcache通过将key转化为hash整数,从而定位到具体的shard分片,继而将值量化为具体的BytesQueue中的偏移量,实现了map[int][int]结构,从而实现了零GC。

这里面的BytesQueue也挺有趣的,将所有的键值数据通过byte的形式与偏移量进行具体的转化,从而实现了基于bytes数组的fifo队列。

另外bigcache还提供除了一组http的接口用于其他服务调用获取缓存数据。

参考

  1. Writing a very fast cache service with millions of entries in Go
  2. bigcache
  3. runtime: Large maps cause significant GC pauses

这篇关于golang本地缓存库之bigcache的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/926712

相关文章

使用Spring Cache本地缓存示例代码

《使用SpringCache本地缓存示例代码》缓存是提高应用程序性能的重要手段,通过将频繁访问的数据存储在内存中,可以减少数据库访问次数,从而加速数据读取,:本文主要介绍使用SpringCac... 目录一、Spring Cache简介核心特点:二、基础配置1. 添加依赖2. 启用缓存3. 缓存配置方案方案

使用Java读取本地文件并转换为MultipartFile对象的方法

《使用Java读取本地文件并转换为MultipartFile对象的方法》在许多JavaWeb应用中,我们经常会遇到将本地文件上传至服务器或其他系统的需求,在这种场景下,MultipartFile对象非... 目录1. 基本需求2. 自定义 MultipartFile 类3. 实现代码4. 代码解析5. 自定

Java实现本地缓存的四种方法实现与对比

《Java实现本地缓存的四种方法实现与对比》本地缓存的优点就是速度非常快,没有网络消耗,本地缓存比如caffine,guavacache这些都是比较常用的,下面我们来看看这四种缓存的具体实现吧... 目录1、HashMap2、Guava Cache3、Caffeine4、Encache本地缓存比如 caff

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

全面解析Golang 中的 Gorilla CORS 中间件正确用法

《全面解析Golang中的GorillaCORS中间件正确用法》Golang中使用gorilla/mux路由器配合rs/cors中间件库可以优雅地解决这个问题,然而,很多人刚开始使用时会遇到配... 目录如何让 golang 中的 Gorilla CORS 中间件正确工作一、基础依赖二、错误用法(很多人一开

java如何实现高并发场景下三级缓存的数据一致性

《java如何实现高并发场景下三级缓存的数据一致性》这篇文章主要为大家详细介绍了java如何实现高并发场景下三级缓存的数据一致性,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 下面代码是一个使用Java和Redisson实现的三级缓存服务,主要功能包括:1.缓存结构:本地缓存:使

Apache Ignite缓存基本操作实例详解

《ApacheIgnite缓存基本操作实例详解》文章介绍了ApacheIgnite中IgniteCache的基本操作,涵盖缓存获取、动态创建、销毁、原子及条件更新、异步执行,强调线程池注意事项,避免... 目录一、获取缓存实例(Getting an Instance of a Cache)示例代码:二、动态

golang程序打包成脚本部署到Linux系统方式

《golang程序打包成脚本部署到Linux系统方式》Golang程序通过本地编译(设置GOOS为linux生成无后缀二进制文件),上传至Linux服务器后赋权执行,使用nohup命令实现后台运行,完... 目录本地编译golang程序上传Golang二进制文件到linux服务器总结本地编译Golang程序

golang版本升级如何实现

《golang版本升级如何实现》:本文主要介绍golang版本升级如何实现问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录golanwww.chinasem.cng版本升级linux上golang版本升级删除golang旧版本安装golang最新版本总结gola

golang中reflect包的常用方法

《golang中reflect包的常用方法》Go反射reflect包提供类型和值方法,用于获取类型信息、访问字段、调用方法等,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值... 目录reflect包方法总结类型 (Type) 方法值 (Value) 方法reflect包方法总结