BigCache

快速、并发、驱逐的内存缓存写入以保留大量条目而不影响性能。 BigCache 将条目保留在堆上,但省略了 GC。 为了实现这一点,需要对字节片进行操作,因此在大多数用例中都需要缓存前面的条目(反)序列化。

简单使用

数据结构

可以用下图来表示
BigCache 中有数组 cacheShard 里面是操作实际的地方,cacheShard 中有两个比较重要的数据结构,HashMap 用来存储Key的索引地址,Entries 用来存储实际的数据,底层是[]byte
notion image

原理分析

设置

设置缓存的时候,会先计算出key对应的hash值,找到key对应的分片位置。之后的操作就在分片shard中操作

查询

查询缓存的时候,会先计算出key对应的hash值,找到key对应的分片位置。之后的操作就在分片shard中操作, 分片shard 会根据计算出来的 index,去 entries 里面找对应的 entry

删除

对应的Key的删除其实就是把 entries 中 arrary 对应的 itemIndex 上置为0。其实这种做法并没有正在删除数据,只是置为0,实际的内存并没有归还,但是把 s.hashmap 中的key对应的 index 删除了。这就做了假删除。用户已经查询不到这个数据了

缓存过期

bigCache 可以为插入的数据设置过期时间,但是缺点是所有数据的过期时间都是一样的。bigCache 中自动删除数据有两种场景:
  • 在插入数据时删除过期数据
  • 通过设置 CleanWindow,启动 goroutine 后台定时批量删除过期数据

实际存储设计

bigCache并没有直接使用类似map中的value中进行存储,而是存储在自己写的BytesQueue中

bigCache 存储

bigCache 扩容

如何做到高性能

加速并发访问

在 bigCache 中, 缓存使用了分段(shard)存储,每一个shard有一个lock,减小了lock的粒度

避免高额的GC开销

在bigCache中,map中没有使用指针,在 Golang(>1.4) 中,如果map中不包含指针的话,GC 便会忽略这个 map。
在bigCache中,bigCache将数据存储在BytesQueue中,BytesQueue的底层结构是[]byte ,这样只给GC增加了一个额外对象,
由于字节切片除了自身对象并不包含其他指针数据,所以GC对于整个对象的标记时间是O(1)的。

参考