当你不断往内存添加数据时,大规模的数据往往对于缓存的效率有很大影响。同时,由大量数据操作引发的垃圾回收(GC)也会对拉低性能,甚至引起不可预料的问题。BigCache,这个能够在内存支持千兆级别数据的缓存,对于GC进行了优化,极大地提高了性能。
简介
Big cache ,是 allegro 在 Github 上开源的Go语言缓存库,项目仓库位于 ,目前版本为 2.2.2。BigCache 是在内存上的缓存,其使用了Go语言1.5所带来的特性,利用了 map[uint64]uint32 类型来避免GC带来的性能损耗,使得其在存储大量数据的同时保持高性能。
安装
BigCache 目前需要 Go 1.12 或以上,使用 go get 直接安装:
go get -u github.com/allegro/bigcache
示例
BigCache的基本使用和其他缓存库基本相同,一个简单的使用例子如下:
import "github.com/allegro/bigcache"
cache, _ := bigcache.NewBigCache(bigcache.Default config (10 * time.Minute))
cache.Set("my-unique-key", [] byte ("value"))
entry, _ := cache.Get("my-unique-key")
fmt.Println(string(entry))
首先进行了BigCache的实例化,使用默认配置,设置可清除时间为10分钟。然后,使用 Set 接口进行了一个key-value形式的缓存项的存储,再使用 Get 接口进行读取,最后输出。
对于不同的应用场景,我们可以进行更为细化的配置:
import (
"log"
"github.com/allegro/bigcache"
)
Config := bigcache.Config {
Shards: 1024,
LifeWindow: 10 * time.Minute,
CleanWindow: 5 * time.Minute,
MaxEntriesInWindow: 1000 * 10 * 60,
MaxEntrySize: 500,
Verbose: true,
HardMaxCacheSize: 8192,
OnRemove: nil,
OnRemoveWithReason: nil,
}
cache, initErr := bigcache.NewBigCache(config)
if initErr != nil {
log.Fatal(initErr)
}
cache.Set("my-unique-key", []byte("value"))
if entry, err := cache.Get("my-unique-key"); err == nil {
fmt.Println(string(entry))
}
具体的配置项包括:
- Shards:切片数,必须为2的整数幂;
- LifeWindow:数据可被清除(驱逐)的时间;
- CleanWindow:扫描清除的时间间隔;
- MaxEntriesInWindow:初始的数据规模;
- MaxEntrySize:初始的数据字节大小;
- Verbose:是否输出运行信息;
- HardMaxCacheSize:硬性的最大规模;
- OnRemove:清除数据的回调;
- OnRemoveWithReason:包括清除原因的清除回调。
在使用时可以根据自身的运行环境进行配置,得到更高的性能效率。
性能测试
BigCache 进行了与 freecache 和 原生map 的性能测试比较,测试结果如下:
go version
go version go1.13 linux/amd64
go test -bench=. -benchmem -benchtime=4s ./... -timeout 30m
goos: linux
goarch: amd64
pkg: github.com/allegro/bigcache/v2/caches_bench
BenchmarkMapSet-8 12999889 376 ns/op 199 B/op 3 allocs/op
BenchmarkConcurrentMapSet-8 4355726 1275 ns/op 337 B/op 8 allocs/op
BenchmarkFreeCacheSet-8 11068976 703 ns/op 328 B/op 2 allocs/op
BenchmarkBigCacheSet-8 10183717 478 ns/op 304 B/op 2 allocs/op
BenchmarkMapGet-8 16536015 324 ns/op 23 B/op 1 allocs/op
BenchmarkConcurrentMapGet-8 13165708 401 ns/op 24 B/op 2 allocs/op
BenchmarkFreeCacheGet-8 10137682 690 ns/op 136 B/op 2 allocs/op
BenchmarkBigCacheGet-8 11423854 450 ns/op 152 B/op 4 allocs/op
BenchmarkBigCacheSetParallel-8 34233472 148 ns/op 317 B/op 3 allocs/op
BenchmarkFreeCacheSetParallel-8 34222654 268 ns/op 350 B/op 3 allocs/op
BenchmarkConcurrentMapSetParallel-8 19635688 240 ns/op 200 B/op 6 allocs/op
BenchmarkBigCacheGetParallel-8 60547064 86.1 ns/op 152 B/op 4 allocs/op
BenchmarkFreeCacheGetParallel-8 50701280 147 ns/op 136 B/op 3 allocs/op
BenchmarkConcurrentMapGetParallel-8 27353288 175 ns/op 24 B/op 2 allocs/op
PASS
ok github.com/allegro/bigcache/v2/caches_bench 256.257s
BigCache在读和写都比freecache更快。
另外,BigCache还对于GC停止时间进行了测试:
go version
go version go1.13 linux/amd64
go run caches_gc_overhead_comparison.go
Number of entries: 20000000
GC pause for bigcache: 1.506077ms
GC pause for freecache: 5.594416ms
GC pause for map: 9.347015ms
go version
go version go1.13 linux/arm64
go run caches_gc_overhead_comparison.go
Number of entries: 20000000
GC pause for bigcache: 22.382827ms
GC pause for freecache: 41.264651ms
GC pause for map: 72.236853ms
可以看到,BigCache拥有最短的GC停止时间。
总结
BigCache充分利用了Go语言的GC策略,实现了在存储大量数据的同时保持高速的读写性能,对于大规模数据的单节点缓存场景有很大的应用价值。
BigCache的代码中包含了大量的并发、锁、内存操作和信道消息相关逻辑,对于对底层操作感兴趣的开发者而言值得研究学习,其特殊的GC机制也有进一步的研究价值。