BigCache针对Go垃圾回收的设计优化

为什么有这篇文章

某一天在群里摸鱼的时候,看到群里有人问go map的空间回收问题,把截图贴上吧:BigCache针对Go垃圾回收的设计优化

其实一位群友发出的问题引起了我注意,他的问题是:gomap的值调用了delete函数是不是不会立即删除?当然这个问题如果研究过或者深入go的内存分配或者说有了解过gogc应该知道,这个问题答案。

关于是分段锁的应用和怎么去优化gc带来的影响,有一个开源项目bigcache在这方面做的比较好。

BigCache的设计

!! 官方作者介绍:快速,并发,基于内存的缓存库,由于是嵌入式库也省去了网络上的开销,完全基于本地内存,能保存大量数据项的同时并且对go语言的garbage collection进行了优化。

对并发锁的颗粒度减小,并且对gc优化它怎么做的?

  1. 分段锁
  2. 数据二进制存储,避免让gc去嵌套扫描
  3. 加速并发访问
  4. 避免高额的GC开销

官方在他们blog上列出了,他们当时写这个库的需求:

  • 处理10k rps (写5000,读5000)
  • cache对象至少存活10分钟
  • 更快的响应时间
  • POST请求的每条 JSON 消息,一有含有ID,二不大于500字节
  • POST请求添加缓存后,GET能获取到最新结果

Go map 的问题

我在网上看到资料有提到一个问题:https://github.com/golang/go/issues/9477

BigCache针对Go垃圾回收的设计优化
问题

大致看了一下这个说了问题就是,Go 1.31.4RC1垃圾回收在扫描一个大的map时需要50-70ms时间,问官方怎么能有什么办法减小这个时间。

然后1.5版本,如果 mapkeyvalue 中都不含指针, GC 便会忽略这个 map

主要的也就两个结构体cachecacheShard:

type cache struct {
    shards []*cacheShard // 块map 解决并发锁颗粒度问题
    hash   fnv64a // 哈希函数
}

源码中的哈希函数用的是fnv64a算法,这个算法的好处是采用位运算的方式在栈上进行运算,避免在堆上分配。

BigCache针对Go垃圾回收的设计优化

shards用来保存cacheShard的,而cacheShard也就是具体存储数据的缓存块,hash会对输入的key进行计算得到cacheShard坐标拿到具体的cacheShard,每个cacheShard都有自己的锁所以才能保证小锁颗粒度。

type cacheShard struct {
    hashmap     map[uint64]uint32 // 外部索引
    entries     queue.BytesQueue // 存储序列化也就是byte形式存储的数据
    lock        sync.RWMutex // 单个锁
    entryBuffer []byte
    onRemove    onRemoveCallback // 移除回调函数

    isVerbose    bool
    statsEnabled bool
    logger       Logger
    clock        clock
    lifeWindow   uint64

    hashmapStats map[uint64]uint32
    stats        Stats
}

cacheShard 中的hashmap结构的类型map[uint64]uint32,完全和key使用的string 扯不上关系,有经验的老司机一眼就看出这其实是个索引,uint64存储的外部键的哈希值,而后面的uint32用处是存储值的位置,而真正的数据存储在BytesQueue中,通过外部索引的值uint32类型来获取里面的值所在的位置索引,而BytesQueue又是经过二进制序列化的数据,取的时候提供外部索引得到坐标取值,最为妙处的设计就是外部索引不存储指针,也不存储符合结构,使得 garbage collection的影响降到最小。

BigCache针对Go垃圾回收的设计优化
BytesQueue

BytesQueue结构体中的array是存储主要数据的byte数组,capacity是使用容量,maxCapacity在创建的时候可以指定最大容量,tail类似链表里面的尾指针,下次插入值可以通过这个指针找到插入的位置,count当前存储的数据条目数,headerBuffer是起到了一个切片发送拷贝时充当零时缓冲区的作用。

有兴趣自己去看源代码吧:https://github.com/allegro/bigcache

总结

BigCache的设计真的很妙,当然这个妙首先你得了解go的gc一些工作方式,然后针对这个这些特定,去优化数据结构,BigCache为了减小锁的颗粒度使用了分段分片,然后防止gc对数据进行扫描的耗时,有把数据采用无指针嵌套的二进制方式去存储,能避免gc去针对底层的数据进行引用存活相关的检测耗时,唯一缺点就是BigCache数据不能持久化存储。


原文始发于微信公众号(TPaper):BigCache针对Go垃圾回收的设计优化

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/23658.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!