关于go库singleflight的使用问题

问题:并发获取数据的时候超过一个请求访问到数据库了
环境:8核cpu,centos7,go1.14

代码:

package main

import (
    "errors"
    "log"
    "sync"
    "golang.org/x/sync/singleflight"
)

var errorNotExist = errors.New("not exist")
var g singleflight.Group

func main() {
    var wg sync.WaitGroup
    cpu_amount := 4
    wg.Add(cpu_amount)

    for i := 0; i < cpu_amount; i++ {
        go func() {
            defer wg.Done()
            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
}

执行结果1:
image.png

执行结果2:
image.png

执行结果3:
image.png

阅读 313
评论
    1 个回答
    • 519

    singleflight 只是避免“同时”高并发的访问,也就是说,只有4个协程都在运行的时候,才满足“同时”的条件。
    你看看 singleflight 的例子就明白了,例子里面都有一句

    time.Sleep(time.Millisecond * 500)

    why? 这就保证所有启动的协程都在同时运行。

    而你的 getDataFromDB,输出一条信息后就结束了,也就是说如果前面三个协程都执行完了,第四个来执行了,当然就没有“同时”性了,也不会导致访问击穿,就会进入到 Do 里面去执行。

      相似问题
      推荐文章