缓存击穿问题是指:在高并发的场景中,大量的请求同时查询一个 key ,如果这个 key 正好过期失效了,就会导致大量的请求都打到数据库,导致数据库的连接增多,负载上升。
安装singleflight
go get -u golang.org/x/sync/singleflight
基本使用
package main
import (
"fmt"
"golang.org/x/sync/singleflight"
"log"
"math/rand"
"sync"
)
var singleHandle singleflight.Group
func main() {
group := sync.WaitGroup{}
for i := 0; i < 10; i++ { // 模拟10个并发
group.Add(1)
go func(i int) {
defer group.Done()
fmt.Printf("goroutine #%d got result: %d\n", i, GetValue("key"))
}(i)
}
group.Wait()
fmt.Println("全部执行完毕")
}
func GetValue(key string) int {
//
v, err, shared := singleHandle.Do(key, func() (interface{}, error) {
log.Printf("getting %s from database\n", key)
log.Printf("setting %s in cache\n", key)
return rand.Int(), nil
})
if err != nil {
fmt.Printf("GetName error:%s\n", err)
return 0
}
fmt.Printf("shared: %v ", shared)
return v.(int)
}
以上代码,模拟 10 个协程请求获取一个 key 的值,代码很简单,就是执行Do()方法。其中,接收两个参数,第一个参数是获取资源的标识,可以是 redis 中缓存的 key 或者数据库的字段,第二个参数就是一个匿名函数,封装好要做的业务逻辑。最终获得的结果如下:
2022/04/29 15:59:53 getting key from database
2022/04/29 15:59:53 setting key in cache
shared: true goroutine #0 got result: 5577006791947779410
shared: true goroutine #8 got result: 5577006791947779410
shared: true goroutine #9 got result: 5577006791947779410
shared: true goroutine #1 got result: 5577006791947779410
shared: true goroutine #2 got result: 5577006791947779410
shared: true goroutine #3 got result: 5577006791947779410
shared: true goroutine #4 got result: 5577006791947779410
shared: true goroutine #5 got result: 5577006791947779410
shared: true goroutine #6 got result: 5577006791947779410
shared: true goroutine #7 got result: 5577006791947779410
全部执行完毕
从上面结果可以看出,10个协程都获得了同一个结果,也就是只有一个协程真正执行了rand.Int()获取了随机数,其他的协程的shared: true
说明都共享了这个结果。
返回值 shared 指示是否将 v 提供给多个调用者。