正常缓存没有数据时会先从DB取数据来回填缓存,而如果瞬间查询过多或者缓存利用率过低。
singlefly
当瞬间过多查询到缓存的空值时就会一起去查询数据库,带给数据库压力变大。这里不能直接用 mutex,如果用了拿不到资源的会自旋等待,拿到后继续查 DB,用 mutex 可能会出现整个逻辑处于一直查 DB 的状态。
// Cache 是一个简单的缓存结构 type Cache struct { mu sync.RWMutex data map[string]string group singleflight.Group } // NewCache 初始化一个新的 Cache 实例 func NewCache() *Cache { return &Cache{ data: make(map[string]string), } } // Get 从缓存中获取值,如果值不存在则调用 fetcher 函数来更新缓存 func (c *Cache) Get(key string, fetcher func() (string, error)) (string, error) { c.mu.RLock() value, exists := c.data[key] c.mu.RUnlock() if exists { return value, nil } // 使用 singleflight 确保只有一个请求会触发 fetcher 调用 result, err, _ := c.group.Do(key, func() (interface{}, error) {
// 假设这个 fetcher 方法就是从数据库拿数据 value, err := fetcher() if err != nil { return nil, err } // 假装这里在回填缓存 c.mu.Lock() c.data[key] = value c.mu.Unlock() return value, nil }) if err != nil { return "", err } return result.(string), nil }
空缓存设置
当数据库没有的数据的查询会反复查询数据库,这时可以在缓存中设置空值进行防护。
// 使用 singleflight 确保只有一个请求会触发 fetcher 调用 result, err, _ := c.group.Do(key, func() (interface{}, error) { value, err := fetcher() if err != nil { // 对于获取失败的情况,不缓存空值 return nil, err } c.mu.Lock() // 如果数据库返回空结果,缓存一个特殊的空值 if value == "" { c.data[key] = "" c.mu.Unlock() return "", errors.New("fetched but empty value") } c.data[key] = value c.mu.Unlock() return value, nil }) if err != nil { return "", err }
标签:缓存,return,nil,err,redis,value,singlefly,string From: https://www.cnblogs.com/caiawo/p/18225697