网络编程
位置:首页>> 网络编程>> Go语言>> golang 防缓存击穿singleflight的实现

golang 防缓存击穿singleflight的实现

作者:xc_oo  发布时间:2024-05-09 09:55:23 

标签:golang,防缓存击穿,singleflight

一、什么是缓存击穿

当一个key是热点key时,一般会做缓存来抗大量并发,但当缓存失效的一瞬间,这些大量的并发请求会击穿缓存,直接请求数据库

为了避免缓存击穿,一种解决方法可以设置缓存永不过期,另一种可以使用golang的包 singleflight 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

来源:https://juejin.cn/post/7012936620177358879

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com