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

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

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

相关文章

20240330-2-XGBoost面试题

XGBoost面试题 1. RF和GBDT的区别 相同点: 都是由多棵树组成,最终的结果都是由多棵树一起决定。 不同点: 集成学习: R F RF RF属于 B a g g i n g Bagging Bagging思想,而 G B D T GBDT GBDT是 B o o s t i n g Bo…

常见排序算法(插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序,计数排序,基数排序,桶排序)

一.排序的概念 1.排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作 2.稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排…

el-menu 有一级二级三级菜单

效果如下 菜单代码如下 <el-menu:default-active"menuDefaultActive"class"el-menu-box":text-color"menuTextColor":active-text-color"menuActiveTextColor":unique-opened"true"><!-- 一级菜单 --><tem…

FastChat启动与部署通义千问大模型

FastChat简介 FastChat is an open platform for training, serving, and evaluating large language model based chatbots. FastChat powers Chatbot Arena, serving over 10 million chat requests for 70 LLMs.Chatbot Arena has collected over 500K human votes from sid…

React Ant Design 简单实现如何选中图片

效果&#xff1a; 代码&#xff1a; 定义的初始值和方法 const [selected, setSelected] useState(0); // 表示当前选中的图片索引const handleClick (index) > {if (selected index) {setSelected(null); // 如果点击的是已选中的图片&#xff0c;则取消选中状态} else…

边缘计算的优势

边缘计算的优势 边缘计算是一种在数据生成地点附近处理数据的技术&#xff0c;而非传统的将数据发送到远端数据中心或云进行处理。这种计算模式对于需要快速响应的场景特别有效&#xff0c;以下详述了边缘计算的核心优势。 1. 降低延迟 边缘计算通过在数据源近处处理数据&…

C# Solidworks二次开发:访问平面、曲面相关API详解

大家好&#xff0c;今天要介绍的是关于平面、曲面相关的API。 下面是相关的API: &#xff08;1&#xff09;第一个为ISurfacePlanarFeatureData&#xff0c;这个API的含义为允许访问平面表面特征&#xff0c;下面是官方的具体解释&#xff1a; 下面是官方使用的例子&#xff…

c语言中的数组

数组 数组是一种构造类型&#xff0c;是由基本类型构造而成。当我们想用一群变量来描述同一类相同的东西时候&#xff0c;比如100个年龄变量&#xff0c;我们可以这样int age1&#xff1b;int age2&#xff1b;int age3&#xff1b;……int age 100;这样即不方便书写&#xff…

【Linux开发 第九篇】磁盘分区

Linux磁盘分区 磁盘分区 Linux分区是用来组成整个文件系统的一部分 Linux采用了一种叫载入的处理方法&#xff0c;它的整个文件系统中包括了一整套的文件和目录&#xff0c;且将一个分区和一个目录联系起来&#xff0c;这时要载入的一个分区将使它的存储空间开一个目录下获得…

便携的图像背景去除工具PhotoScissors9.2版本在Windows系统的下载与安装配置

目录 前言一、PhotoScissors安装二、使用配置总结 前言 ​“ PhotoScissors是一个多功能和强大的照片编辑工具&#xff0c;专门为Windows用户寻求一个直观的解决方案&#xff0c;背景删除和图像编辑。作为专用的Windows软件&#xff0c;PhotoScissors提供了一个用户友好的平台…

Error in render: TypeError: Cannot read properties of undefined (reading‘‘)

报错内容 报错解释&#xff1a;这个错误在渲染过程中尝试读取一个未定义&#xff08;undefined&#xff09;对象的某个属性时发生了TypeError。具体来说&#xff0c;是尝试读取一个值为undefined的对象的某个属性&#xff0c;但该属性不存在&#xff0c;因此无法读取。解决过程…

【剪映专业版】15绿幕抠图:应用技巧、注意事项

视频课程&#xff1a;B站有知公开课【剪映电脑版教程】 1.绿幕抠图 背景为绿色的素材&#xff08;其他颜色也可以扣取&#xff0c;只是绿色背景更普遍、抠图效果更好&#xff09; 选择色度抠图&#xff0c;通过取色器选取背景颜色&#xff0c;调整强度实现抠图 剪映的色度抠…