github地址:https://github.com/allegro/bigcache
简介
先扒一波官方文档凑字数
Fast, concurrent, evicting in-memory cache written to keep big number of entries without impact on performance. BigCache keeps entries on heap but omits GC for them. To achieve that, operations on byte slices take place, therefore entries (de)serialization in front of the cache will be needed in most use cases.
机器翻译:快速,并发,驱逐内存中的高速缓存,以保持大量的条目不影响性能.BigCache将条目保存在堆中,但忽略了它们的GC。为此,对字节片进行操作,因此在大多数用例中都需要缓存前面的条目(反序列化)。
虽然redis具有非常不错的性能(官方数据读的速度是110000次/秒,写的速度是81000次/秒),但是这并不等于我们一个进程在一秒可以对redis做好几万次操作。比如,现在基本都是用TCP协议建立与本地redis的连接,那么每次对redis进行操作都要把数据打包发到运输层,运输层再发到网络层,然后才返回发到本地的redis进程上。虽然使用pipeline可以减少一部分I/O时间消耗,但总归还是有性能瓶颈,很难达到每秒几万的操作效率。
因此,在没有使用分布式服务器的情况下,进程内缓存就显得很好用了。
此外,bigcache还拥有以下非常好用的特性:
- 即使拥有百万记录也非常快(主要用哈希实现)
- 提供并发访问
- 在预定的时间后移除记录
简单使用
又来扒拉一波官方文档
1 | import "github.com/allegro/bigcache" |
使用该库主要有以下几个特点
- 主要操作只有Set、Get、Delete、Len
- 只能存取[]byte类型
- config对象的设置对速度的影响较大
- 需要用自定义的config时需要实现Hasher接口
config对象简介
1 | // Config for BigCache |
优化的一些原理
1、并发优化
由于bigcache支持并发,因此势必会用锁(事实用的是读写锁)。在高并发场景下,如果只用一把读写锁,势必会显著影响读写速度。解决方案是用分块的思想(想必大家在数据结构上都听老师口糊过),把整个内存分成N个部分,每个部分单独分配一把锁保证并发安全。在哈希函数足够优秀的情况下,就可以实现同时允许好几个协程进行读写。
所以这部分的优化主要是N的选择以及哈希函数的选择。
(1)N的选择
首先N肯定不是越大越好,因为太大会带来很多不必要的开销(增加内存使用和寻找所在分块的时间),且性能提升也未必会很明显,所以要在性能和花销上寻找一个平衡。框架作者推荐的值是1024。
另外,N的值最好是2的幂,因为这样底层可以直接用位运算取模,进一步减少时间开销。
(2)Hash算法的选择
太玄学了还是算了
看了半天还是用他内置的FNV64a算法吧。
2、GC优化
Go1.5对GC进行了优化,如果map中的键值对都是基本类型,则GC会忽略他们。
因此框架作者利用这一特性,在每个分块中用map[uint64]uint32和一个预先分配的大字节数组(数组还是切片暂时没有证实)存储数据,把缓存对象序列化的后放到预先分配好的大字节数组中,把它的哈希值作为map[uint64]uint32的key,然后把它在数组中的offset作为map[uint64]uint32的value。