为什么你绝对不该用go语言的Singleflight来防止缓存击穿
目前发现,关于go语言库Singleflight,网络上充斥着大量错误的看法。如用来防止缓存击穿,甚至有说用这个库减少对缓存的调用的。对不知道这个库的观众,简单介绍一下这个库
Singleflight是Go语言标准库golang.org/x/sync/singleflight
中的一个包,主要用于防止重复计算。多个请求通过singleflight发出时,相同的请求最终只有一个会执行。
import (
"golang.org/x/sync/singleflight"
)
var group singleflight.Group
func getData(key string) (string, error) {
// 使用Do方法确保对同一key只有一个请求实际执行
result, err, _ := group.Do(key, func() (interface{}, error) {
// 这里执行实际的获取数据操作
return doSomething(key)
})
return result.(string), err
}
其他后到请求会直接共享执行线程的结果,一次实际上只有一个请求被执行了。实际上如果用这种方式来控制请求会产生问题。例如
其中singleflight作为单独的中间层来拦截get请求,并将单次请求的结果返回给所有调用的线程或节点。调用者2由于是set请求,并不需要通过singleflight。
图中singleflight中间件已经向数据库发起了请求。但是由于网络等原因,这个包可能被延迟返回给所有调用它的节点。图中的
的红线表示被singleflight合并的请求,图中x的坐标表示时间轴。在红色时间轴内的请求都会被合并返回结果为第一个请求。调用者3的所有请求都应该读取调用者2设置之后的值。按照相对时间来说调用者3在得知调用者2完成设置之后才发起了请求,但其请求确得到了一个更早的值。这种错误通常被认为是不能接受的。
有趣的是在知乎,阿里云上都看到这个错误的用法。