1. 介绍 BigCache
是一个快速,支持并发访问,自淘汰的内存型缓存,可以在存储大量元素的同时依然保持高性能。BigCache
将元素保存在堆上却避免了GC
的开销。源码地址:https://github.com/allegro/bigcache
1.1 为什么开发bigcache
? bigcache
团队接到一个任务,需要开发一个非常快速的缓存服务,并满足以下几点需求:
使用 HTTP 协议处理请求。
处理10k rps
(写5000,读5000)。
cache
对象至少存活10分钟。
更快的响应时间。
POST
请求的每条 JSON
消息,一有含有ID,二不大于500字节.
POST
请求添加缓存后,GET
能获取到最新结果。
简单地说,我们的任务是编写一个带有过期和 REST 接口的快速字典。
1.2 为什么不用第三方服务? 为了满足上述任务需求,要求开发的cache
库要保证:
即使有百万的缓存对象也要非常快
支持大并发访问
一定时间后支持剔除
官方原文:
Considering the first point we decided to give up external caches like Redis , Memcached or Couchbase mainly because of additional time needed on the network. Therefore we focused on in-memory caches. In Go there are already caches of this type, i.e. LRU groups cache , go-cache , ttlcache , freecache . Only freecache fulfilled our needs. Next subchapters reveal why we decided to roll our own anyway and describe how the characteristics mentioned above were achieved.
翻译后:
考虑到第一点,我们决定放弃外部缓存,如 Redis,Memcached 或 Couchbase 主要是因为额外的时间需要在网络上。因此,我们主要关注内存缓存。在 Go 中已经有这种类型的缓存,如 LRU groups cache
、 Go-cache
、 ttlcache
和 freecache
。只有 freecache
满足了我们的需要。接下来的分章揭示了为什么我们决定滚动我们自己的无论如何,并描述了如何实现上面提到的特点。
2. 安装 go get -u github.com/allegro/bigcache
3. 初始化 3.1 默认初始化 a. 代码示例
func TestInitDefaultCache (t *testing.T) { cache, _ := bigcache.NewBigCache(bigcache.DefaultConfig(time.Second * 5 )) defer cache.Close() err := cache.Set("key1" , []byte ("hello word" )) if err != nil { t.Errorf("设置缓存失败:%v" ,err) } data, err := cache.Get("key1" ) if err != nil { t.Errorf("获取缓存失败:%v" ,err) } fmt.Printf("获取结果:%s\n" ,data) }
b. 输出
=== RUN TestInitDefaultCache 获取结果:hello word --- PASS: TestInitDefaultCache (0.03s) PASS
3.2 自定义初始化 a. 代码示例
func TestInitCustom (t *testing.T) { config := bigcache.Config{ Shards: 1024 , LifeWindow: 5 * time.Second, CleanWindow: 3 * time.Second, MaxEntriesInWindow: 1 , MaxEntrySize: 500 , Verbose: true , HardMaxCacheSize: 8192 , OnRemove: callBack, OnRemoveWithReason: nil , } cache,err := bigcache.NewBigCache(config) if err != nil { t.Error(err) } defer cache.Close() _ = cache.Set("key1" , []byte ("hello word" )) time.Sleep(10 * time.Second) data, err := cache.Get("key1" ) if err != nil { t.Errorf("获取缓存失败:%v" ,err) } fmt.Printf("获取结果:%s\n" ,data) fmt.Println("运行结束!" ) }
b.输出
=== RUN TestInitCustom 过期回调: key=key1 val=hello word bigcache_test.go:74: 获取缓存失败:Entry not found 获取结果: 运行结束! --- FAIL: TestInitCustom (10.00s)
在实际使用中发现,只设置CleanWindow = n
,缓存并不一定会在n
秒后自动删除,需要结合 LifeWindow
。如CleanWindow = 4s LifeWindow=3s 代表 4s后会删除LifeWindow中已经被标记为不活跃的缓存(有效期为3s)
4.使用 4.1 添加和获取( Set|Get
) func TestSetAndGet (t *testing.T) { cache, _ := bigcache.NewBigCache(bigcache.DefaultConfig(time.Minute)) err := cache.Set("key1" , []byte ("php" )) if err != nil { t.Errorf("设置缓存失败:%v" ,err) } _ = cache.Set("key2" ,[]byte ("go" )) for _, key := range []string {"key1" ,"key2" } { if data, err := cache.Get(key);err == nil { fmt.Printf("key: %s 结果:%s\n" ,key,data) } } }
4.2 删除缓存(Delete
) func TestDelCache (t *testing.T) { cache, _ := bigcache.NewBigCache(bigcache.DefaultConfig(time.Minute)) key := "key" _ = cache.Set(key,[]byte ("111" )) _ = cache.Delete(key) if _, err := cache.Get(key);err != nil { fmt.Println(err) } }
4.3 长度和容量(Len|Capacity
) func TestLenAndCap (t *testing.T) { cache, _ := bigcache.NewBigCache(bigcache.DefaultConfig(time.Minute)) _ = cache.Set("key" , []byte ("1" )) _ = cache.Set("key1" , []byte ("1" )) _ = cache.Set("key2" , []byte ("1" )) _ = cache.Set("key3" , []byte ("1" )) fmt.Printf("缓存数量: %d \n" , cache.Len()) fmt.Printf("缓存容量: %d \n" , cache.Capacity()) }
4.4 重置(Reset
) func TestReset (t *testing.T) { cache, _ := bigcache.NewBigCache(bigcache.DefaultConfig(time.Minute)) for i := 0 ; i < 10 ; i++ { k := fmt.Sprintf("key%d" ,i) _ = cache.Set(k,[]byte (strconv.Itoa(i))) } fmt.Printf("重置前缓存数量: %d \n" , cache.Len()) _ = cache.Reset() fmt.Printf("重置后缓存数量: %d \n" , cache.Len()) }