redis 缓存 singlefly 查询

正常缓存没有数据时会先从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
    })

 

posted @ 2024-06-01 11:17  Fang20  阅读(3)  评论(0编辑  收藏  举报