golang 防缓存击穿singleflight的实现

 更新时间:2022年08月02日 08:55:26   作者: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 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解golang consul-grpc 服务注册与发现

    详解golang consul-grpc 服务注册与发现

    这篇文章主要介绍了详解golang consul-grpc 服务注册与发现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • 在go语言中安装与使用protobuf的方法详解

    在go语言中安装与使用protobuf的方法详解

    protobuf以前只支持C++, Python和Java等语言, Go语言出来后, 作为亲儿子, 那有不支持的道理呢? 这篇文章主要给大家介绍了关于在go语言中使用protobuf的相关资料,文中介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-08-08
  • Go语言Gin框架前后端分离项目开发实例

    Go语言Gin框架前后端分离项目开发实例

    本文主要介绍了Go语言Gin框架前后端分离项目开发工程化实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-11-11
  • Go语言模型:string的底层数据结构与高效操作详解

    Go语言模型:string的底层数据结构与高效操作详解

    这篇文章主要介绍了Go语言模型:string的底层数据结构与高效操作详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 一文带你了解Go语言中函数设计的实践示例

    一文带你了解Go语言中函数设计的实践示例

    良好设计的函数具有清晰的职责和逻辑结构,提供准确的命名和适当的参数控制,下面我们将一一描述函数设计时能够遵循的最佳实践,希望对大家有所帮助
    2023-06-06
  • Go 语言使用goroutine运行闭包踩坑分析

    Go 语言使用goroutine运行闭包踩坑分析

    这篇文章主要介绍了Go 语言使用goroutine运行闭包踩坑解决分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Golang定时任务框架GoCron的源码分析

    Golang定时任务框架GoCron的源码分析

    本文主要介绍了Golang定时任务框架GoCron的源码分析,原生的gocron存在一些问题,如任务列表维护不当、并发操作不可预测等,经过改进的gocron解决了这些问题,感兴趣的可以了解一下
    2025-03-03
  • 一文带你掌握Go语言运算符的使用

    一文带你掌握Go语言运算符的使用

    运算符用于在程序运行时执行数学或逻辑运算。Go 语言内置的运算符有:算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、其他运算符。本文将带大家详细了解一下这些运算符的使用,感兴趣的可以了解一下
    2022-04-04
  • go select编译期的优化处理逻辑使用场景分析

    go select编译期的优化处理逻辑使用场景分析

    select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。接下来通过本文给大家介绍go select编译期的优化处理逻辑使用场景分析,感兴趣的朋友一起看看吧
    2021-06-06
  • golang并发执行的几种方式小结

    golang并发执行的几种方式小结

    本文主要介绍了golang并发执行的几种方式小结,主要包括了Channel,WaitGroup ,Context,使用这三种机制中的一种或者多种可以达到并发控制很好的效果,具有一定的参考价值,感兴趣的可以了解一下
    2023-08-08

最新评论