目录
一、什么是缓存击穿
当一个key是热点key时,一般会做缓存来抗大量并发,但当缓存失效的一瞬间,这些大量的并发请求会击穿缓存,直接请求数据库
golang.org/x/sync/singleflight
二、原理
多个并发请求对一个失效key进行数据获取时,只会有其中一个去直接获取数据,其它请求会阻塞等待第一个请求返回给它们结果
三、实现
package singleflight import ( "sync" ) var WaitCount int var DirectCount int type Caller struct { val interface{} err error wg sync.WaitGroup } type Group struct { mu sync.RWMutex m map[string]*Caller } func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) { g.mu.Lock() if g.m == nil { g.m = make(map[string]*Caller) } c, ok := g.m[key] if ok { //阻塞等待其它已经执行此操作的返回结果 g.mu.Unlock() c.wg.Wait() WaitCount++ return c.val, c.err } //直接请求获取数据 c = &Caller{} g.m[key] = c c.wg.Add(1) g.mu.Unlock() c.val, c.err = fn() c.wg.Done() g.mu.Lock() delete(g.m, key) g.mu.Unlock() DirectCount++ return c.val, c.err }
测试:
func TestGroup_Do(t *testing.T) { sg := &Group{} wg := sync.WaitGroup{} for i := 0; i < 10000; i++ { fn := func() (interface{}, error) { return i, nil } wg.Add(1) go func() { defer wg.Done() got, err := sg.Do("test-key", fn) _, _ = got, err //t.Log("got:", i) }() } wg.Wait() fmt.Println("waitCount:", WaitCount) fmt.Println("DirectCount:", DirectCount) }
输出:
waitCount: 8323
DirectCount: 1401