婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > 使用Golang的singleflight防止緩存擊穿的方法

使用Golang的singleflight防止緩存擊穿的方法

熱門標(biāo)簽:智能電銷機(jī)器人營銷 澳門防封電銷卡 福州鐵通自動外呼系統(tǒng) 長沙ai機(jī)器人電銷 地圖標(biāo)注測試 賺地圖標(biāo)注的錢犯法嗎 烏魯木齊人工電銷機(jī)器人系統(tǒng) 廣東語音外呼系統(tǒng)供應(yīng)商 濮陽自動外呼系統(tǒng)代理

在使用緩存時,容易發(fā)生緩存擊穿。

緩存擊穿:一個存在的key,在緩存過期的瞬間,同時有大量的請求過來,造成所有請求都去讀dB,這些請求都會擊穿到DB,造成瞬時DB請求量大、壓力驟增。

singleflight

介紹

import "golang.org/x/sync/singleflight"

singleflight類的使用方法就新建一個singleflight.Group,使用其方法Do或者DoChan來包裝方法,被包裝的方法在對于同一個key,只會有一個協(xié)程執(zhí)行,其他協(xié)程等待那個協(xié)程執(zhí)行結(jié)束后,拿到同樣的結(jié)果。

Group結(jié)構(gòu)體

代表一類工作,同一個group中,同樣的key同時只能被執(zhí)行一次。

Do方法

func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool)

key:同一個key,同時只有一個協(xié)程執(zhí)行。

fn:被包裝的函數(shù)。

v:返回值,即執(zhí)行的結(jié)果。其他等待的協(xié)程都會拿到。

shared:表示是否有其他協(xié)程得到了這個結(jié)果v。

DoChan方法

func (g *Group) DoChan(key string, fn func() (interface{}, error)) -chan Result

與Do方法一樣,只是返回的是一個channel,執(zhí)行結(jié)果會發(fā)送到channel中,其他等待的協(xié)程都可以從channel中拿到結(jié)果。

ref:https://godoc.org/golang.org/x/sync/singleflight

示例

使用Do方法來模擬,解決緩存擊穿的問題

func main() {

  var singleSetCache singleflight.Group

  getAndSetCache:=func (requestID int,cacheKey string) (string, error) {

  log.Printf("request %v start to get and set cache...",requestID)

  value,_, _ :=singleSetCache.Do(cacheKey, func() (ret interface{}, err error) {//do的入?yún)ey,可以直接使用緩存的key,這樣同一個緩存,只有一個協(xié)程會去讀DB

    log.Printf("request %v is setting cache...",requestID)

     time.Sleep(3*time._Second_)

     log.Printf("request %v set cache success!",requestID)

    return "VALUE",nil

   })

  return value.(string),nil

  }

  cacheKey:="cacheKey"

  for i:=1;i10;i++{//模擬多個協(xié)程同時請求

  go func(requestID int) {

     value,_:=getAndSetCache(requestID,cacheKey)

     log.Printf("request %v get value: %v",requestID,value)

   }(i)

  }

  time.Sleep(20*time._Second_)
}

輸出:

2020/04/12 18:18:40 request 4 start  to  get  and  set cache...

2020/04/12 18:18:40 request 4 is setting cache...

2020/04/12 18:18:40 request 2 start  to  get  and  set cache...

2020/04/12 18:18:40 request 7 start  to  get  and  set cache...

2020/04/12 18:18:40 request 5 start  to  get  and  set cache...

2020/04/12 18:18:40 request 1 start  to  get  and  set cache...

2020/04/12 18:18:40 request 6 start  to  get  and  set cache...

2020/04/12 18:18:40 request 3 start  to  get  and  set cache...

2020/04/12 18:18:40 request 8 start  to  get  and  set cache...

2020/04/12 18:18:40 request 9 start  to  get  and  set cache...

2020/04/12 18:18:43 request 4 set  cache  success!

2020/04/12 18:18:43 request 4 get value: VALUE

2020/04/12 18:18:43 request 9 get value: VALUE

2020/04/12 18:18:43 request 6 get value: VALUE

2020/04/12 18:18:43 request 3 get value: VALUE

2020/04/12 18:18:43 request 8 get value: VALUE

2020/04/12 18:18:43 request 1 get value: VALUE

2020/04/12 18:18:43 request 5 get value: VALUE

2020/04/12 18:18:43 request 2 get value: VALUE

2020/04/12 18:18:43 request 7 get value: VALUE`

可以看到確實只有一個協(xié)程執(zhí)行了被包裝的函數(shù),并且其他協(xié)程都拿到了結(jié)果。

源碼分析

看一下這個Do方法是怎么實現(xiàn)的。

首先看一下Group的結(jié)構(gòu):

type Group struct {

  mu sync.Mutex   

  m map[string]*call //保存key對應(yīng)的函數(shù)執(zhí)行過程和結(jié)果的變量。

}

Group的結(jié)構(gòu)非常簡單,一個鎖來保證并發(fā)安全,另一個map用來保存key對應(yīng)的函數(shù)執(zhí)行過程和結(jié)果的變量。

看下call的結(jié)構(gòu):

type call struct {

  wg sync.WaitGroup //用WaitGroup實現(xiàn)只有一個協(xié)程執(zhí)行函數(shù)

  val interface{} //函數(shù)執(zhí)行結(jié)果

  err error

  forgotten bool

  dups int //含義是duplications,即同時執(zhí)行同一個key的協(xié)程數(shù)量

  chans []chan- Result
}

看下Do方法

func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) {

  g.mu.Lock()//寫Group的m字段時,加鎖保證寫安全。

  if g.m == nil {

  g.m = make(map[string]*call)

  }

if c, ok := g.m[key]; ok {//如果key已經(jīng)存在,說明已經(jīng)有協(xié)程在執(zhí)行,則dups++,并等待其執(zhí)行完畢后,返回其執(zhí)行結(jié)果,執(zhí)行結(jié)果保存在對應(yīng)的call的val字段里

   c.dups++

   g.mu.Unlock()

   c.wg.Wait()

 return c.val, c.err, true

  }

//如果key不存在,則新建一個call,并使用WaitGroup來阻塞其他協(xié)程,同時在m字段里寫入key和對應(yīng)的call

c := new(call)

  c.wg.Add(1)

  g.m[key] = c

  g.mu.Unlock()

  g.doCall(c, key, fn)//第一個進(jìn)來的協(xié)程來執(zhí)行這個函數(shù)

return c.val, c.err, c.dups > 0

}

繼續(xù)看下g.doCall里具體干了什么

func (g *Group) doCall(c *call, key string, fn func() (interface{}, error)) {

  c.val, c.err = fn()//執(zhí)行被包裝的函數(shù)

  c.wg.Done()//執(zhí)行完畢后,就可以通知其他協(xié)程可以拿結(jié)果了

  g.mu.Lock()

if !c.forgotten {//其實這里是為了保證執(zhí)行完畢之后,對應(yīng)的key被刪除,Group有一個方法Forget(key string),可以用來主動刪除key,這里是判斷那個方法是否被調(diào)用過,被調(diào)用過則字段forgotten會置為true,如果沒有被調(diào)用過,則在這里把key刪除。

  delete(g.m, key)

  }

  for _, ch := range c.chans {//將執(zhí)行結(jié)果發(fā)送到channel里,這里是給DoChan方法使用的

  ch - Result{c.val, c.err, c.dups > 0}

  }

  g.mu.Unlock()

}

由此看來,其實現(xiàn)是非常簡單的。不得不贊嘆一百來行代碼就實現(xiàn)了功能。

其他

順便附上DoChan方法的使用示例:

func main() {

  var singleSetCache singleflight.Group

  getAndSetCache:=func (requestID int,cacheKey string) (string, error) {

  log.Printf("request %v start to get and set cache...",requestID)

  retChan:=singleSetCache.DoChan(cacheKey, func() (ret interface{}, err error) {

    log.Printf("request %v is setting cache...",requestID)

    time.Sleep(3*time._Second_)

    log.Printf("request %v set cache success!",requestID)

    return "VALUE",nil

   })

  var ret singleflight.Result

  timeout := time.After(5 * time._Second_)

  select {//加入了超時機(jī)制

    case -timeout:

      log.Printf("time out!")

      return "",errors.New("time out")

    case ret =- retChan://從chan中取出結(jié)果

      return ret.Val.(string),ret.Err

   }

  return "",nil

  }

  cacheKey:="cacheKey"
  
  for i:=1;i10;i++{

  go func(requestID int) {

     value,_:=getAndSetCache(requestID,cacheKey)

     log.Printf("request %v get value: %v",requestID,value)

   }(i)

  }

  time.Sleep(20*time._Second_)

}

看下DoChan的源碼

func (g *Group) DoChan(key string, fn func() (interface{}, error)) -chan Result {

  ch := make(chan Result, 1)

  g.mu.Lock()

  if g.m == nil {

  g.m = make(map[string]*call)

  }

  if c, ok := g.m[key]; ok {

   c.dups++

c.chans = append(c.chans, ch)//可以看到,每個等待的協(xié)程,都有一個結(jié)果channel。從之前的g.doCall里也可以看到,每個channel都給塞了結(jié)果。為什么不所有協(xié)程共用一個channel?因為那樣就得在channel里塞至少與協(xié)程數(shù)量一樣的結(jié)果數(shù)量,但是你卻無法保證用戶一個協(xié)程只讀取一次。

   g.mu.Unlock()

   return ch

  }

  c := call{chans: []chan- Result{ch}}

  c.wg.Add(1)

  g.m[key] = c

  g.mu.Unlock()

  go g.doCall(c, key, fn)

  return ch
}

到此這篇關(guān)于使用Golang的singleflight防止緩存擊穿的方法的文章就介紹到這了,更多相關(guān)Golang singleflight防止緩存擊穿內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

您可能感興趣的文章:
  • 一篇文章帶你輕松了解C# Lock關(guān)鍵字
  • C#筆試題之同線程Lock語句遞歸不會死鎖
  • C#使用Interlocked實現(xiàn)線程同步
  • C#中使用Interlocked進(jìn)行原子操作的技巧
  • C#中實現(xiàn)線程同步lock關(guān)鍵字的用法詳解
  • 如何使用C#讀寫鎖ReaderWriterLockSlim
  • C#中的lock、Monitor、Mutex學(xué)習(xí)筆記
  • C#中l(wèi)ock用法詳解
  • C#中l(wèi)ock死鎖實例教程
  • c#多線程中Lock()關(guān)鍵字的用法小結(jié)
  • c# 如何用lock解決緩存擊穿

標(biāo)簽:太原 慶陽 西雙版納 貴陽 阿克蘇 調(diào)研邀請 德州 廣西

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《使用Golang的singleflight防止緩存擊穿的方法》,本文關(guān)鍵詞  使用,Golang,的,singleflight,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《使用Golang的singleflight防止緩存擊穿的方法》相關(guān)的同類信息!
  • 本頁收集關(guān)于使用Golang的singleflight防止緩存擊穿的方法的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    国产一区二区三区在线观看免费视频 | 亚洲成人av一区二区三区| 激情小说欧美图片| 26uuu久久综合| 国产成人午夜精品影院观看视频 | 91在线码无精品| 国产拍揄自揄精品视频麻豆| 久久疯狂做爰流白浆xx| 精品久久久久99| 国模套图日韩精品一区二区| 久久综合久久鬼色中文字| 国产精品1区2区| 日韩美女视频一区二区| 91福利在线观看| 免费一级片91| 中文字幕av资源一区| 一本到高清视频免费精品| 亚洲香肠在线观看| 久久综合久久综合亚洲| 91小视频免费看| 午夜私人影院久久久久| 日韩一级视频免费观看在线| 国内外精品视频| 亚洲激情中文1区| 69p69国产精品| jvid福利写真一区二区三区| 亚洲成人动漫av| 国产午夜亚洲精品午夜鲁丝片| 色婷婷亚洲婷婷| 激情综合五月天| 亚洲综合激情另类小说区| 精品国产1区二区| 色综合欧美在线视频区| 久久 天天综合| 亚洲综合999| 久久久噜噜噜久久人人看 | 男女男精品网站| 国产精品久久久久久久久晋中| 欧美久久久久久久久久| 成人小视频免费观看| 亚洲成av人综合在线观看| 中文字幕乱码日本亚洲一区二区| 日本道精品一区二区三区| 狠狠色丁香九九婷婷综合五月| 亚洲激情校园春色| 欧美精品一区二区三区在线| 欧美制服丝袜第一页| 高清beeg欧美| 激情综合色播激情啊| 一区二区不卡在线播放| 中日韩av电影| 欧美xxxxx牲另类人与| 在线精品视频一区二区三四| 国产综合久久久久久久久久久久| 一区二区三区在线视频免费观看| 久久久久久久综合狠狠综合| 欧美怡红院视频| av色综合久久天堂av综合| 精品一区二区在线视频| 日韩av一二三| 亚洲18影院在线观看| 一区二区三区在线播| 综合中文字幕亚洲| 中文字幕欧美日韩一区| 国产亚洲一区二区在线观看| 精品久久久久一区| 337p日本欧洲亚洲大胆色噜噜| 日韩欧美中文字幕一区| 精品国产一区二区三区不卡| 欧美一卡2卡3卡4卡| 日韩一二三区不卡| 欧美tickling挠脚心丨vk| 日韩一区二区三区四区五区六区| 91精品国模一区二区三区| 欧美一区二区观看视频| 欧美成人欧美edvon| 久久综合九色综合欧美98| 久久九九久久九九| 欧美国产成人在线| 亚洲欧美在线高清| 亚洲精品国产成人久久av盗摄 | 国产成人av影院| 岛国av在线一区| 不卡视频免费播放| 欧美午夜一区二区| 日韩精品中午字幕| 国产精品久久久久影视| 亚洲激情中文1区| 日韩成人av影视| 国产伦理精品不卡| 日本韩国欧美国产| 日韩欧美另类在线| 亚洲视频网在线直播| 视频一区视频二区中文字幕| 看片的网站亚洲| 99国产精品久久久| 91精品久久久久久久久99蜜臂| 欧美变态口味重另类| 国产精品美女久久久久高潮| 亚洲亚洲人成综合网络| 久久99精品国产91久久来源| 97成人超碰视| 欧美成人bangbros| 亚洲精品亚洲人成人网在线播放| 美国十次综合导航| 91麻豆国产自产在线观看| 日韩欧美在线123| 亚洲欧美偷拍三级| 久久99最新地址| 色八戒一区二区三区| 久久日一线二线三线suv| 一区二区三区精品久久久| 狠狠色丁香久久婷婷综合_中| 色成年激情久久综合| 国产欧美一区二区精品性| 热久久免费视频| 在线观看国产精品网站| 国产精品免费视频一区| 久久超碰97人人做人人爱| 欧美亚日韩国产aⅴ精品中极品| 国产亚洲欧洲一区高清在线观看| 亚洲超碰97人人做人人爱| 一本久道久久综合中文字幕| 久久久久99精品一区| 另类小说色综合网站| 欧美日韩在线播放一区| 一区二区三区欧美日韩| av中文字幕在线不卡| 国产亚洲精久久久久久| 精品一区精品二区高清| 精品美女在线观看| 日韩中文字幕一区二区三区| 久久久噜噜噜久噜久久综合| 美国一区二区三区在线播放| 9191成人精品久久| 日韩精品一卡二卡三卡四卡无卡| 在线观看亚洲精品| 亚洲一级二级三级在线免费观看| 97se亚洲国产综合自在线| 国产精品另类一区| 波多野结衣中文一区| 中文字幕制服丝袜成人av| 99精品国产99久久久久久白柏| 国产精品短视频| 91在线观看下载| 亚洲另类春色国产| 色狠狠综合天天综合综合| 亚洲欧美偷拍三级| 在线播放欧美女士性生活| 日韩1区2区日韩1区2区| 欧美videofree性高清杂交| 国内精品久久久久影院色| 久久色在线视频| 91无套直看片红桃| 欧美韩日一区二区三区| 国产精品一区二区久激情瑜伽| 国产日韩欧美精品一区| 丰满放荡岳乱妇91ww| 亚洲欧美另类久久久精品| 91精品国产综合久久香蕉麻豆| 蜜臀国产一区二区三区在线播放| 精品福利视频一区二区三区| 国产精品自在在线| 亚洲欧美日本韩国| 欧美一区二区三区的| 精品一区二区三区不卡| 中文字幕一区二区三区不卡在线| 色又黄又爽网站www久久| 美女视频黄免费的久久 | 丁香婷婷综合色啪| 综合电影一区二区三区| 欧美视频一区二| 国产一区二区按摩在线观看| 国产精品麻豆欧美日韩ww| 欧美日韩在线精品一区二区三区激情| 久久se精品一区二区| 亚洲欧美在线aaa| 日韩欧美一区中文| 久久久久久久久岛国免费| 国产精品国产三级国产aⅴ中文 | 亚洲午夜激情av| 久久久久99精品一区| 欧美日韩免费一区二区三区| 国产一区999| 亚洲国产成人精品视频| 国产亚洲欧美日韩俺去了| 色伊人久久综合中文字幕| 精品一区精品二区高清| 91久久精品国产91性色tv| 日韩免费性生活视频播放| 日韩制服丝袜av| 欧美激情艳妇裸体舞| 中文字幕日本乱码精品影院| 国产日产欧产精品推荐色| xnxx国产精品| 激情成人综合网| 夜夜亚洲天天久久| 国产精品乱码一区二区三区软件 | 国产毛片精品国产一区二区三区| 亚洲男人的天堂av|