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

相关文章

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包方法总结

一文详解Git中分支本地和远程删除的方法

《一文详解Git中分支本地和远程删除的方法》在使用Git进行版本控制的过程中,我们会创建多个分支来进行不同功能的开发,这就容易涉及到如何正确地删除本地分支和远程分支,下面我们就来看看相关的实现方法吧... 目录技术背景实现步骤删除本地分支删除远程www.chinasem.cn分支同步删除信息到其他机器示例步骤

Golang如何对cron进行二次封装实现指定时间执行定时任务

《Golang如何对cron进行二次封装实现指定时间执行定时任务》:本文主要介绍Golang如何对cron进行二次封装实现指定时间执行定时任务问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录背景cron库下载代码示例【1】结构体定义【2】定时任务开启【3】使用示例【4】控制台输出总结背景

Golang如何用gorm实现分页的功能

《Golang如何用gorm实现分页的功能》:本文主要介绍Golang如何用gorm实现分页的功能方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录背景go库下载初始化数据【1】建表【2】插入数据【3】查看数据4、代码示例【1】gorm结构体定义【2】分页结构体

在Golang中实现定时任务的几种高效方法

《在Golang中实现定时任务的几种高效方法》本文将详细介绍在Golang中实现定时任务的几种高效方法,包括time包中的Ticker和Timer、第三方库cron的使用,以及基于channel和go... 目录背景介绍目的和范围预期读者文档结构概述术语表核心概念与联系故事引入核心概念解释核心概念之间的关系

前端如何通过nginx访问本地端口

《前端如何通过nginx访问本地端口》:本文主要介绍前端如何通过nginx访问本地端口的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、nginx安装1、下载(1)下载地址(2)系统选择(3)版本选择2、安装部署(1)解压(2)配置文件修改(3)启动(4)

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

Golang 日志处理和正则处理的操作方法

《Golang日志处理和正则处理的操作方法》:本文主要介绍Golang日志处理和正则处理的操作方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录1、logx日志处理1.1、logx简介1.2、日志初始化与配置1.3、常用方法1.4、配合defer