会不会当清理启动时, 锁定了 go-cache.map (注意这个时候是写锁), 由于 go-cache.map 中元素过多, 导致 map 一直被锁定, 那么这个时候所有的 Set 函数是不是就会产生 Lock 竞争?
使用 go-cache 的时候, 当某个接口的 QPS 很高, 程序里由于使用问题, 将某些不该往 go-cache 存的 value 也存了进去, 那么会不会导致 Set 之间的 Lock 竞争呢?
场景还原利用下面的程序可以轻松还原上面的问题场景. 上面提出的问题, 都会造成 go-cache lock 竞争. 这里利用 pprof 查看程序的指标
var goroutineNums = flag.Int("gn", 2, "goroutine nums") func main() { flag.Parse() go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }() rand.Seed(time.Now().Unix()) lc := cache.New(time.Minute*5, time.Minute*2) log.Printf("start at:%v", time.Now()) aaaKey := "aaa:%d:buy:cnt" log.Println("set run over") for i := 0; i < *goroutineNums; i++ { go func(idx int) { for { key := fmt.Sprintf(aaaKey, rand.Int()) newKey := fmt.Sprintf("%s:%d", key, rand.Int()) v := rand.Int() lc.Set(newKey, v, time.Millisecond) } }(i) } // 查看 go-cache 中 key 的数量 go func() { ticker := time.NewTicker(time.Second) for { select { case <-ticker.C: log.Printf("lc size:%d", lc.ItemCount()) } } }() select {} } 模拟接口高QPS ./go-cache-test -gn 2000 2020/03/12 00:32:33 start at:2020-03-12 00:32:33.949073 +0800 CST m=+0.001343027 2020/03/12 00:32:34 lc size:538398 2020/03/12 00:32:35 lc size:1149109瞬间就会出现锁竞争
模拟 go-cache 启动清理时的情形 ./go-cache-test -gn 2 2020/03/12 00:37:33 start at:2020-03-12 00:37:33.171238 +0800 CST m=+0.001457393 ... 2020/03/12 00:40:35 lc size:54750220 2020/03/12 00:40:35 start clear at:2020-03-12 00:40:35.103586 +0800 CST m=+120.005547323 2020/03/12 00:41:51 lc size:33 2020/03/12 00:41:51 lc size:50会看到当清理 map 的时候, 如果 map 中的数据过多就会造成 Lock 竞争, 造成其他数据无法写入 map
总结 我使用的问题背景: 某接口 QPS 有点高
当时考虑到用户购买状态(这个状态可能随时变化)如果能够在本地缓存中缓存 10s, 那么用户再次点进来的时候能从本地取了, 就造成大量的数据都写入了 map 中
由于接口 QPS 比较高, 设置用户购买状态时就可能造成竞争, 造成接口响应超时
go-cache 使用注意点尽量存放那些相对不怎么变化的数据, 适用于所有的 local cache(包括 map, sync.map)
go-cache 的过期检查时间要设置相对较小, 也不能过小
那些高 QPS 的接口尽量不要去直接 Set 数据, 如果必须 Set 可以采用异步操作
监控 go-cache 里面 key 的数量, 如果过多时, 需要及时调整参数
资料go-cache
bigcache
freecache
runtime.SetFinalizer