缓存击穿,是指一个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)机制,防止缓存击穿相关推荐

  1. Redis支持的数据类型以及使用场景,持久化,哨兵机制,缓存击穿,缓存穿透

    Redis支持的数据类型以及使用场景,持久化,哨兵机制,缓存击穿,缓存穿透 简单介绍一个redis? redis是内存中的数据结构存储系统,一个key-value类型的非关系型数据库,可持久化的数据库 ...

  2. mysql缓存淘汰机制_Redis缓存总结:淘汰机制、缓存雪崩、数据不一致....

    在实际的工作项目中, 缓存成为高并发.高性能架构的关键组件 ,那么Redis为什么可以作为缓存使用呢?首先可以作为缓存的两个主要特征: 在分层系统中处于内存/CPU具有访问性能良好, 缓存数据饱和,有 ...

  3. redis 查询缓存_Redis缓存总结:淘汰机制、缓存雪崩、数据不一致....

    在实际的工作项目中, 缓存成为高并发.高性能架构的关键组件 ,那么Redis为什么可以作为缓存使用呢?首先可以作为缓存的两个主要特征: 在分层系统中处于内存/CPU具有访问性能良好, 缓存数据饱和,有 ...

  4. 浏览器缓存机制(强缓存和协商缓存)总结

    文章目录 前言 缓存过程的分析 强缓存 Expires Cache-Control 协商缓存 Last-Modified和If-Modified-Since ETag和If-None-Match 两者 ...

  5. Redis详解(redis线程模式、数据持久化机制、主从复制、缓存穿透、缓存击穿等)

    一.redis概述 redis主要用作数据库.缓存和消息中间件, 支持多种语言, 是基于内存的key-value数据结构存储系统. redis支持数据的持久化, 可以将内存中的数据保存在磁盘中, 重启 ...

  6. 《如何与面试官处朋友》系列-缓存击穿、穿透、雪崩场景原理大调解

    前面我们提到分布式多级缓存架构的全貌,但总感觉少了些什么东西.在这样大的场景下面,如果遇到缓存使用问题那可咋办?但自古英雄出少年,相信此刻你已踏马西去,正走在寻找答案上得夕阳西下.每每面谈Redis大 ...

  7. 布隆过滤器解决缓存穿透_缓冲穿透/缓存击穿/缓存雪崩等问题解决办法

    ----------------------[感谢小昭提供的图片] 1.缓存穿透 定义:请求查询数据库中压根就不存在的数据,每次请求直接打在DB上,这种查询查询不存在数据的现象称为缓存穿透 穿透带来的 ...

  8. Redis的缓存雪崩、缓存击穿、缓存穿透与缓存预热、缓存降级

    一.缓存雪崩: 1.什么是缓存雪崩: 如果缓在某一个时刻出现大规模的key失效,那么就会导致大量的请求打在了数据库上面,导致数据库压力巨大,如果在高并发的情况下,可能瞬间就会导致数据库宕机.这时候如果 ...

  9. 面试必会系列 - 3.1 Redis知识点大汇总(数据类型,内存模型,持久化,缓存击穿,集群,一致性哈希等等)

    本文已收录至 Github(MD-Notes),若博客中图片模糊或打不开,可以来我的 Github 仓库,包含了完整图文:https://github.com/HanquanHq/MD-Notes,涵 ...

最新文章

  1. 关于程序猿的几个阶段!
  2. 微服务集成cas_Spring Cloud Security集成CAS (单点登录)对微服务认证
  3. ionic1 打包过程 常用命令行
  4. Visual Guide to NoSQL Systems
  5. 腾讯云TStack获“下一代云计算技术创新奖”,助力云生态信息创新发展
  6. Oracle密码过期问题 ORA-28001:the password has expired
  7. idiom的学习笔记(一)、三栏布局
  8. c++将小写转换为大写函数_必须掌握的基础函数组合应用技巧,提高效率,准时下班...
  9. redhat6 忘记密码怎么办
  10. keil4出现目标未被创建_STM32入门系列-创建寄存器模板
  11. Atitit dsl实现(1)------异常的库模式实现  异常的ast结构
  12. 计算机课怎么管纪律,浅谈如何巧妙设置学生机轻松管理机房课堂纪律
  13. Palm应用开发之三appinfo.json 文件详解
  14. SWF播放器object DEMO
  15. 多线程练习题(双色球)
  16. 生态参数反演(植被覆盖度FVC)手把手教会
  17. oracle如何实现累乘,由复合指标计算引起的oracle累乘
  18. As Manufacturers Buckle, Winners Emerge From Havoc
  19. 区块链与区块链平台的工作流程
  20. ssb的matlab仿真,单边带调制(SSB调制)的理论基础和MATLAB仿真

热门文章

  1. mac配置环境变量不生效
  2. Java8新特性总结 -8.Nashorn , JavaScript引擎
  3. JAVA大厂高频面试题及答案
  4. linux 25端口漏洞,一封突如其来的邮件443端口漏洞
  5. cdn厂商 同兴万点_CDN进入牌照时代 工信部规范和清理CDN市场
  6. Kafka是如何处理Netflix每天2万亿条消息的?
  7. 浏览器内核之WebKit 架构与模块
  8. ambassador 学习七 Mapping说明
  9. 定义软件定义的存储市场
  10. 《为iPad而设计:打造畅销App》——快速回顾用户界面设计史