golang groupcache重复抑制(singeflight)机制,防止缓存击穿
缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
groupcache是golang实现的分布式缓存,和memcache同一作者出品,groupcache使用重复抑制机制(singeflight)用了很少了代码提供了缓存击穿的解决方式。代码github地址:https://github.com/golang/groupcache/blob/master/singleflight/singleflight.go
代码分析
// Group represents a class of work and forms a namespace in which
// units of work can be executed with duplicate suppression.
type Group struct {
mu sync.Mutex // protects m
m map[string]*call // lazily initialized
}
Group表示一个工作类,并形成一个命名空间,在该命名空间中,可以使用重复抑制来执行工作单元。
Group中的map是执行的命名空间,mu则保证map的安全性。
// call is an in-flight or completed Do call
type call struct {
wg sync.WaitGroup
val interface{}
err error
}
call结构体可以保存执行的结果,并使用WaitGroup让并发获取结果的其他goroutine可以等待执行的携程执行完并获取结果。
val和err则保存了执行结果和错误。
// Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
if c, ok := g.m[key]; ok {
g.mu.Unlock()
c.wg.Wait()
return c.val, c.err
}
c := new(call)
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
c.val, c.err = fn()
c.wg.Done()g.mu.Lock()
delete(g.m, key)
g.mu.Unlock()return c.val, c.err
}
执行并返回给定函数的结果,确保一次只执行一个给定key的函数。如果出现重复,则重复调用方将等待正在执行的相同key函数完成并接收相同的结果。key用来标识操作,fn则执行操作,如果key当前没在执行的话,封装成call加入命名空间标识该key在执行,WaitGroup加1,然后开始执行,执行完后WaitGroup放开。
如果命名空间中已经存在key了说明该key已经在执行,那么就是使用WaitGroup等待执行结果即可。
代码相当简单通用。
测试用例分析
测试用例分别测试了Do执行功能,Do执行返回错误情形,重复抑制功能。直接在原始代码中加注释来展示。
import (
“errors”
“fmt”
“sync”
“sync/atomic”
“testing”
“time”
)
//测试Do功能,传入一个固定函数,比较返回与预期
func TestDo(t *testing.T) {
var g Group
v, err := g.Do(“key”, func() (interface{}, error) {
return “bar”, nil
})
if got, want := fmt.Sprintf("%v (%T)", v, v), “bar (string)”; got != want {
t.Errorf(“Do = %v; want %v”, got, want)
}
if err != nil {
t.Errorf(“Do error = %v”, err)
}
}
//测试Do返回错误功能,传入一个固定函数,比较错误返回与预期
func TestDoErr(t *testing.T) {
var g Group
someErr := errors.New(“Some error”)
v, err := g.Do(“key”, func() (interface{}, error) {
return nil, someErr
})
if err != someErr {
t.Errorf(“Do error = %v; want someErr”, err)
}
if v != nil {
t.Errorf(“unexpected non-nil value %#v”, v)
}
}
//重复抑制测试
//
func TestDoDupSuppress(t *testing.T) {
var g Group
c := make(chan string)
//记录调用次数
var calls int32
//每调用一次calls加一
fn := func() (interface{}, error) {
atomic.AddInt32(&calls, 1)
return <-c, nil
}
const n = 10
var wg sync.WaitGroup
for i := 0; i < n; i++ {wg.Add(1)go func() {v, err := g.Do("key", fn)//每一个goroutine判断结果是否符合预期if err != nil {t.Errorf("Do error: %v", err)}if v.(string) != "bar" {t.Errorf("got %q; want %q", v, "bar")}wg.Done()}()
}
time.Sleep(100 * time.Millisecond) // let goroutines above block
//睡眠让所有goroutine执行到等待结果
//传入值,函数执行结束
c <- "bar"
//等待全部执行结束
wg.Wait()
//判断是否执行一次
if got := atomic.LoadInt32(&calls); got != 1 {t.Errorf("number of calls = %d; want 1", got)
}
}
另外,golang还提供了一个更通用单飞版本,地址:https://github.com/golang/sync/tree/master/singleflight
————————————————
版权声明:本文为CSDN博主「AJuTongXue」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/l1002544188/article/details/88780336
golang groupcache重复抑制(singeflight)机制,防止缓存击穿相关推荐
- Redis支持的数据类型以及使用场景,持久化,哨兵机制,缓存击穿,缓存穿透
Redis支持的数据类型以及使用场景,持久化,哨兵机制,缓存击穿,缓存穿透 简单介绍一个redis? redis是内存中的数据结构存储系统,一个key-value类型的非关系型数据库,可持久化的数据库 ...
- mysql缓存淘汰机制_Redis缓存总结:淘汰机制、缓存雪崩、数据不一致....
在实际的工作项目中, 缓存成为高并发.高性能架构的关键组件 ,那么Redis为什么可以作为缓存使用呢?首先可以作为缓存的两个主要特征: 在分层系统中处于内存/CPU具有访问性能良好, 缓存数据饱和,有 ...
- redis 查询缓存_Redis缓存总结:淘汰机制、缓存雪崩、数据不一致....
在实际的工作项目中, 缓存成为高并发.高性能架构的关键组件 ,那么Redis为什么可以作为缓存使用呢?首先可以作为缓存的两个主要特征: 在分层系统中处于内存/CPU具有访问性能良好, 缓存数据饱和,有 ...
- 浏览器缓存机制(强缓存和协商缓存)总结
文章目录 前言 缓存过程的分析 强缓存 Expires Cache-Control 协商缓存 Last-Modified和If-Modified-Since ETag和If-None-Match 两者 ...
- Redis详解(redis线程模式、数据持久化机制、主从复制、缓存穿透、缓存击穿等)
一.redis概述 redis主要用作数据库.缓存和消息中间件, 支持多种语言, 是基于内存的key-value数据结构存储系统. redis支持数据的持久化, 可以将内存中的数据保存在磁盘中, 重启 ...
- 《如何与面试官处朋友》系列-缓存击穿、穿透、雪崩场景原理大调解
前面我们提到分布式多级缓存架构的全貌,但总感觉少了些什么东西.在这样大的场景下面,如果遇到缓存使用问题那可咋办?但自古英雄出少年,相信此刻你已踏马西去,正走在寻找答案上得夕阳西下.每每面谈Redis大 ...
- 布隆过滤器解决缓存穿透_缓冲穿透/缓存击穿/缓存雪崩等问题解决办法
----------------------[感谢小昭提供的图片] 1.缓存穿透 定义:请求查询数据库中压根就不存在的数据,每次请求直接打在DB上,这种查询查询不存在数据的现象称为缓存穿透 穿透带来的 ...
- Redis的缓存雪崩、缓存击穿、缓存穿透与缓存预热、缓存降级
一.缓存雪崩: 1.什么是缓存雪崩: 如果缓在某一个时刻出现大规模的key失效,那么就会导致大量的请求打在了数据库上面,导致数据库压力巨大,如果在高并发的情况下,可能瞬间就会导致数据库宕机.这时候如果 ...
- 面试必会系列 - 3.1 Redis知识点大汇总(数据类型,内存模型,持久化,缓存击穿,集群,一致性哈希等等)
本文已收录至 Github(MD-Notes),若博客中图片模糊或打不开,可以来我的 Github 仓库,包含了完整图文:https://github.com/HanquanHq/MD-Notes,涵 ...
最新文章
- 关于程序猿的几个阶段!
- 微服务集成cas_Spring Cloud Security集成CAS (单点登录)对微服务认证
- ionic1 打包过程 常用命令行
- Visual Guide to NoSQL Systems
- 腾讯云TStack获“下一代云计算技术创新奖”,助力云生态信息创新发展
- Oracle密码过期问题 ORA-28001:the password has expired
- idiom的学习笔记(一)、三栏布局
- c++将小写转换为大写函数_必须掌握的基础函数组合应用技巧,提高效率,准时下班...
- redhat6 忘记密码怎么办
- keil4出现目标未被创建_STM32入门系列-创建寄存器模板
- Atitit dsl实现(1)------异常的库模式实现 异常的ast结构
- 计算机课怎么管纪律,浅谈如何巧妙设置学生机轻松管理机房课堂纪律
- Palm应用开发之三appinfo.json 文件详解
- SWF播放器object DEMO
- 多线程练习题(双色球)
- 生态参数反演(植被覆盖度FVC)手把手教会
- oracle如何实现累乘,由复合指标计算引起的oracle累乘
- As Manufacturers Buckle, Winners Emerge From Havoc
- 区块链与区块链平台的工作流程
- ssb的matlab仿真,单边带调制(SSB调制)的理论基础和MATLAB仿真