Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > golang 防缓存击穿singleflight

golang 防缓存击穿singleflight的实现

作者:xc_oo

本文主要介绍了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

到此这篇关于golang 防缓存击穿singleflight的实现的文章就介绍到这了,更多相关golang 防缓存击穿singleflight 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文