Page

【Go】go redis缓存击穿防范措施之singleflight

466Anson20-04-10


go redis缓存击穿防范措施之singleflight

缓存击穿:热key过期,同一时间大量请求进入到数据库


例子1:

package main
import (
    "fmt"
    "sync"
    "time"
    "golang.org/x/sync/singleflight"
)
func main() {
    g := singleflight.Group{}
    wg := sync.WaitGroup{}
    for i := 0; i < 20; i++ {
        wg.Add(1)
        go func(j int) {
            defer wg.Done()
            // The return value shared indicates whether v was given to multiple callers, shared 表示返回 val 是否被多个调用者共享了https://segmentfault.com/q/1010000022916754?_ea=49700336
            val, err, shared := g.Do("userId", queryMysql)
            if err != nil {
                fmt.Println(err)
                return
            }
            fmt.Printf("index: %d, val: %d, shared: %t\n", j, val, shared)
        }(i)
    }
    wg.Wait()
}
// 模拟数据库查询方法
func queryMysql() (interface{}, error) {
    time.Sleep(time.Millisecond * 500)
    fmt.Println("mysql query")
    return 1, nil
}

例子2:

package main
import (
    "errors"
    "log"
    "sync"
    "time"
    "golang.org/x/sync/singleflight"
)
var errorNotExist = errors.New("not exist")
var g singleflight.Group
func main() {
    var wg sync.WaitGroup
    cpu_amount := 8
    wg.Add(cpu_amount)
    for i := 0; i < cpu_amount; i++ {
        go func() {
            defer wg.Done()
            // sleep的作用是确保8个协程同时启动,需要多核cpu
            time.Sleep(1 * time.Second)
            data, err := getData("key")
            if err != nil {
                log.Print(err)
                return
            }
            log.Println(data)
        }()
    }
    wg.Wait()
}
// 获取数据
func getData(key string) (string, error) {
    // 获取缓存
    data, err := getDataFromCache(key)
    if err == errorNotExist {
        v, err, _ := g.Do(key, func() (interface{}, error) {
            // 从数据库获取数据,只有第一个请求会进入数据库,其他请求会复用返回值)
            return getDataFromDB(key)
        })
        if err != nil {
            log.Println(err)
            return "", err
        }
        data = v.(string)
    } else if err != nil {
        return "", err
    }
    return data, nil
}
func getDataFromCache(key string) (string, error) {
    return "", errorNotExist
}
func getDataFromDB(key string) (string, error) {
    log.Printf("get %s from database", key)
    return "data", nil
}

image.png



来自anson博客 

http://www.tp0.top