redis 快取 singlefly 查詢

Fang20發表於2024-06-01

正常快取沒有資料時會先從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
    }

相關文章