精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

讓我們一起賞析Singleflight設計

開發 開發工具
今天想與大家分享一下singleflight這個庫,singleflight僅僅只有100多行卻可以做到防止緩存擊穿,有點厲害哦!所以本文我們就一起來看一看他是怎么設計的~。

[[411393]]

本文轉載自微信公眾號「Golang夢工廠」,作者AsongGo。轉載本文請聯系Golang夢工廠公眾號。

前言

哈嘍,大家好,我是asong。今天想與大家分享一下singleflight這個庫,singleflight僅僅只有100多行卻可以做到防止緩存擊穿,有點厲害哦!所以本文我們就一起來看一看他是怎么設計的~。

注意:本文基于 https://pkg.go.dev/golang.org/x/sync/singleflight進行分析。

緩存擊穿

什么是緩存擊穿

平常在高并發系統中,會出現大量的請求同時查詢一個key的情況,假如此時這個熱key剛好失效了,就會導致大量的請求都打到數據庫上面去,這種現象就是緩存擊穿。緩存擊穿和緩存雪崩有點像,但是又有一點不一樣,緩存雪崩是因為大面積的緩存失效,打崩了DB,而緩存擊穿則是指一個key非常熱點,在不停的扛著高并發,高并發集中對著這一個點進行訪問,如果這個key在失效的瞬間,持續的并發到來就會穿破緩存,直接請求到數據庫,就像一個完好無損的桶上鑿開了一個洞,造成某一時刻數據庫請求量過大,壓力劇增!

如何解決

  • 方法一

我們簡單粗暴點,直接讓熱點數據永遠不過期,定時任務定期去刷新數據就可以了。不過這樣設置需要區分場景,比如某寶首頁可以這么做。

  • 方法二

為了避免出現緩存擊穿的情況,我們可以在第一個請求去查詢數據庫的時候對他加一個互斥鎖,其余的查詢請求都會被阻塞住,直到鎖被釋放,后面的線程進來發現已經有緩存了,就直接走緩存,從而保護數據庫。但是也是由于它會阻塞其他的線程,此時系統吞吐量會下降。需要結合實際的業務去考慮是否要這么做。

  • 方法三

方法三就是singleflight的設計思路,也會使用互斥鎖,但是相對于方法二的加鎖粒度會更細,這里先簡單總結一下singleflight的設計原理,后面看源碼在具體分析。

singleflightd的設計思路就是將一組相同的請求合并成一個請求,使用map存儲,只會有一個請求到達mysql,使用sync.waitgroup包進行同步,對所有的請求返回相同的結果。

截屏2021-07-14 下午8.30.56

源碼賞析

已經迫不及待了,直奔主題吧,下面我們一起來看看singleflight是怎么設計的。

數據結構

singleflight的結構定義如下:

  1. type Group struct { 
  2.  mu sync.Mutex       // 互斥鎖,保證并發安全 
  3.  m  map[string]*call // 存儲相同的請求,key是相同的請求,value保存調用信息。 

Group結構還是比較簡單的,只有兩個字段,m是一個map,key是相同請求的標識,value是用來保存調用信息,這個map是懶加載,其實就是在使用時才會初始化;mu是互斥鎖,用來保證m的并發安全。m存儲調用信息也是單獨封裝了一個結構:

  1. type call struct { 
  2.  wg sync.WaitGroup 
  3.  // 存儲返回值,在wg done之前只會寫入一次 
  4.  val interface{} 
  5.   // 存儲返回的錯誤信息 
  6.  err error 
  7.  
  8.  // 標識別是否調用了Forgot方法 
  9.  forgotten bool 
  10.  
  11.  // 統計相同請求的次數,在wg done之前寫入 
  12.  dups  int 
  13.   // 使用DoChan方法使用,用channel進行通知 
  14.  chans []chan<- Result 
  15. // Dochan方法時使用 
  16. type Result struct { 
  17.  Val    interface{} // 存儲返回值 
  18.  Err    error // 存儲返回的錯誤信息 
  19.  Shared bool // 標示結果是否是共享結果 

Do方法

  1. // 入參:key:標識相同請求,fn:要執行的函數 
  2. // 返回值:v: 返回結果 err: 執行的函數錯誤信息 shard: 是否是共享結果 
  3. func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) { 
  4.  // 代碼塊加鎖 
  5.  g.mu.Lock() 
  6.  // map進行懶加載 
  7.  if g.m == nil { 
  8.    // map初始化 
  9.   g.m = make(map[string]*call) 
  10.  } 
  11.  // 判斷是否有相同請求 
  12.  if c, ok := g.m[key]; ok { 
  13.    // 相同請求次數+1 
  14.   c.dups++ 
  15.   // 解鎖就好了,只需要等待執行結果了,不會有寫入操作了 
  16.   g.mu.Unlock() 
  17.   // 已有請求在執行,只需要等待就好了 
  18.   c.wg.Wait() 
  19.   // 區分panic錯誤和runtime錯誤 
  20.   if e, ok := c.err.(*panicError); ok { 
  21.    panic(e) 
  22.   } else if c.err == errGoexit { 
  23.    runtime.Goexit() 
  24.   } 
  25.   return c.val, c.err, true 
  26.  } 
  27.  // 之前沒有這個請求,則需要new一個指針類型 
  28.  c := new(call) 
  29.  // sync.waitgroup的用法,只有一個請求運行,其他請求等待,所以只需要add(1) 
  30.  c.wg.Add(1) 
  31.  // m賦值 
  32.  g.m[key] = c 
  33.  // 沒有寫入操作了,解鎖即可 
  34.  g.mu.Unlock() 
  35.  // 唯一的請求該去執行函數了 
  36.  g.doCall(c, key, fn) 
  37.  return c.val, c.err, c.dups > 0 

這里是唯一有疑問的應該是區分panic和runtime錯誤部分吧,這個與下面的docall方法有關聯,看完docall你就知道為什么了。

docall

  1. // doCall handles the single call for a key
  2. func (g *Group) doCall(c *call, key string, fn func() (interface{}, error)) { 
  3.   // 標識是否正常返回 
  4.  normalReturn := false 
  5.   // 標識別是否發生panic 
  6.  recovered := false 
  7.    
  8.  defer func() { 
  9.   // 通過這個來判斷是否是runtime導致直接退出了 
  10.   if !normalReturn && !recovered { 
  11.       // 返回runtime錯誤信息 
  12.    c.err = errGoexit 
  13.   } 
  14.  
  15.   c.wg.Done() 
  16.   g.mu.Lock() 
  17.   defer g.mu.Unlock() 
  18.     // 防止重復刪除key 
  19.   if !c.forgotten { 
  20.    delete(g.m, key
  21.   } 
  22.   // 檢測是否出現了panic錯誤 
  23.   if e, ok := c.err.(*panicError); ok { 
  24.    // 如果是調用了dochan方法,為了channel避免死鎖,這個panic要直接拋出去,不能recover住,要不就隱藏錯誤了 
  25.    if len(c.chans) > 0 { 
  26.     go panic(e) // 開一個寫成panic 
  27.     select {} // 保持住這個goroutine,這樣可以將panic寫入crash dump 
  28.    } else { 
  29.     panic(e) 
  30.    } 
  31.   } else if c.err == errGoexit { 
  32.    // runtime錯誤不需要做任何時,已經退出了 
  33.   } else { 
  34.    // 正常返回的話直接向channel寫入數據就可以了 
  35.    for _, ch := range c.chans { 
  36.     ch <- Result{c.val, c.err, c.dups > 0} 
  37.    } 
  38.   } 
  39.  }() 
  40.   // 使用匿名函數目的是recover住panic,返回信息給上層 
  41.  func() { 
  42.   defer func() { 
  43.    if !normalReturn { 
  44.     // 發生了panic,我們recover住,然后把錯誤信息返回給上層 
  45.     if r := recover(); r != nil { 
  46.      c.err = newPanicError(r) 
  47.     } 
  48.    } 
  49.   }() 
  50.   // 執行函數 
  51.   c.val, c.err = fn() 
  52.     // fn沒有發生panic 
  53.   normalReturn = true 
  54.  }() 
  55.  // 判斷執行函數是否發生panic 
  56.  if !normalReturn { 
  57.   recovered = true 
  58.  } 

這里來簡單描述一下為什么區分panic和runtime錯誤,不區分的情況下如果調用出現了恐慌,但是鎖沒有被釋放,會導致使用相同key的所有后續調用都出現了死鎖,具體可以查看這個issue:https://github.com/golang/go/issues/33519。

Dochan和Forget方法

  1. //異步返回 
  2. // 入參數:key:標識相同請求,fn:要執行的函數 
  3. // 出參數:<- chan 等待接收結果的channel 
  4. func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result { 
  5.   // 初始化channel 
  6.  ch := make(chan Result, 1) 
  7.  g.mu.Lock() 
  8.   // 懶加載 
  9.  if g.m == nil { 
  10.   g.m = make(map[string]*call) 
  11.  } 
  12.   // 判斷是否有相同的請求 
  13.  if c, ok := g.m[key]; ok { 
  14.     //相同請求數量+1 
  15.   c.dups++ 
  16.     // 添加等待的chan 
  17.   c.chans = append(c.chans, ch) 
  18.   g.mu.Unlock() 
  19.   return ch 
  20.  } 
  21.  c := &call{chans: []chan<- Result{ch}} 
  22.  c.wg.Add(1) 
  23.  g.m[key] = c 
  24.  g.mu.Unlock() 
  25.  // 開一個寫成調用 
  26.  go g.doCall(c, key, fn) 
  27.  // 返回這個channel等待接收數據 
  28.  return ch 
  29. // 釋放某個 key 下次調用就不會阻塞等待了 
  30. func (g *Group) Forget(key string) { 
  31.  g.mu.Lock() 
  32.  if c, ok := g.m[key]; ok { 
  33.   c.forgotten = true 
  34.  } 
  35.  delete(g.m, key
  36.  g.mu.Unlock() 

注意事項

因為我們在使用singleflight時需要自己寫執行函數,所以如果我們寫的執行函數一直循環住了,就會導致我們的整個程序處于循環的狀態,積累越來越多的請求,所以在使用時,還是要注意一點的,比如這個例子:

  1. result, err, _ := d.singleGroup.Do(key, func() (interface{}, error) { 
  2.   for
  3.    // TODO 
  4.   } 

不過這個問題一般也不會發生,我們在日常開發中都會使用context控制超時。

總結

 

好啦,這篇文章就到這里啦。因為最近我在項目中也使用singleflight這個庫,所以就看了一下源碼實現,真的是厲害,這么短的代碼就實現了這么重要的功能,我怎么就想不到呢。。。。所以說還是要多讀一些源碼庫,真的能學到好多,真是應了那句話:你知道的越多,不知道的就越多!

 

責任編輯:武曉燕 來源: Golang夢工廠
相關推薦

2022-03-31 18:59:43

數據庫InnoDBMySQL

2022-03-08 17:52:58

TCP格式IP

2021-08-27 07:06:10

IOJava抽象

2021-12-29 08:27:05

ByteBuffer磁盤服務器

2021-11-26 07:00:05

反轉整數數字

2022-02-14 10:16:22

Axios接口HTTP

2022-06-26 09:40:55

Django框架服務

2022-02-14 07:03:31

網站安全MFA

2016-09-06 10:39:30

Dell Techno

2023-08-14 08:38:26

反射reflect結構體

2023-08-02 08:35:54

文件操作數據源

2022-07-10 23:15:46

Go語言內存

2022-08-01 07:57:03

數組操作內存

2021-12-16 12:01:21

區塊鏈Libra貨幣

2021-07-31 11:40:55

Openresty開源

2012-04-14 20:47:45

Android

2014-02-25 08:59:14

2022-12-05 09:10:21

2021-11-09 23:54:19

開發SMI Linkerd

2021-02-23 09:21:29

代碼效率C++
點贊
收藏

51CTO技術棧公眾號

91大神在线网站| 樱花视频在线免费观看| 国产香蕉精品| 色综合久久综合网欧美综合网| 日本不卡在线观看| 亚洲视频在线免费播放| 国内精品久久久久久久影视麻豆 | 26uuu另类亚洲欧美日本一| 一本色道综合久久欧美日韩精品| 欧美日韩精品免费观看视完整| 国产精品免费观看视频| 国产一级精品aaaaa看| 在线观看亚洲天堂| 亚洲成av人电影| 亚洲老司机av| 超级砰砰砰97免费观看最新一期 | 特级毛片在线| 久久综合99re88久久爱| 91美女片黄在线观| 国产一区二区视频网站| 欧美午夜不卡影院在线观看完整版免费| 日韩禁在线播放| 亚洲综合伊人久久| 日韩高清不卡| 同产精品九九九| 中文字幕の友人北条麻妃| 欧美捆绑视频| 成人免费视频免费观看| 91日本在线视频| 国产精品xxxxxx| 亚洲视频www| 欧美黑人性视频| 日本少妇aaa| 神马久久一区二区三区| 亚洲国产私拍精品国模在线观看| 亚洲综合123| 久久69成人| 欧美午夜一区二区三区| 国产午夜福利视频在线观看| 青春草视频在线观看| 中文字幕av免费专区久久| 久久久一本精品99久久精品| 丰满肉嫩西川结衣av| 蜜臀久久99精品久久久久宅男 | 99久久久久久99| 成人xxxxx色| 国产乱色精品成人免费视频| 美女视频第一区二区三区免费观看网站 | 欧美自拍丝袜亚洲| 久久精品99国产| 亚洲最大网站| 欧美性猛交xxxxx免费看| aa视频在线播放| 久久香蕉一区| 香蕉影视欧美成人| 日韩中文字幕在线视频观看| 国产丝袜精品丝袜| 亚洲国产综合人成综合网站| 日韩久久久久久久久久久久| 欧美videosex性极品hd| 亚洲午夜影视影院在线观看| 青青草视频在线视频| 国产视频在线播放| 亚洲欧美成人一区二区三区| 日本特级黄色大片| 亚洲大胆人体大胆做受1| 一区二区三区不卡视频| h无码动漫在线观看| 国产蜜臀av在线播放| 亚洲地区一二三色| 国产一区二区在线视频播放| 一个人www视频在线免费观看| 天天av天天翘天天综合网| 91精品91久久久中77777老牛 | 国产精品青青在线观看爽香蕉| 国产一级片免费视频| 另类的小说在线视频另类成人小视频在线| 国产精品久久久久一区二区| 国产精品系列视频| 国产aⅴ综合色| 久久久久se| 成人欧美亚洲| 亚洲乱码日产精品bd| 成年女人18级毛片毛片免费| 日本不良网站在线观看| 欧美性色黄大片| 91欧美一区二区三区| 国产精品欧美大片| 亚洲午夜色婷婷在线| 三级影片在线观看| 99国产成+人+综合+亚洲欧美| 日韩免费在线观看视频| 国产精品久久久久久免费| 国产98色在线|日韩| 你懂的视频在线一区二区| 9191在线观看| 亚洲高清免费视频| 性欧美极品xxxx欧美一区二区| 爱情电影网av一区二区| 日韩精品www| 欧美爱爱免费视频| 欧美亚洲免费| 5g国产欧美日韩视频| 亚洲av激情无码专区在线播放| 国产农村妇女毛片精品久久麻豆| 久久99国产精品一区| 中文字幕在线直播| 91精品国产综合久久婷婷香蕉| 国产麻豆xxxvideo实拍| 91精品天堂福利在线观看| 国产91ⅴ在线精品免费观看| 国产一区二区在线视频观看| 91女厕偷拍女厕偷拍高清| 影音先锋欧美在线| 欧美aaaaa性bbbbb小妇| 欧美精品日韩综合在线| 岛国精品资源网站| 91精品国产自产在线观看永久∴| 欧美性受xxx| 亚洲va欧美va| 亚洲婷婷国产精品电影人久久| 久久网站免费视频| 视频精品一区二区三区| 在线亚洲国产精品网| www.av视频在线观看| 九色porny丨国产精品| 欧美一区二区在线| 曰本三级在线| 欧美夫妻性生活| 色屁屁草草影院ccyy.com| 一本色道久久综合亚洲精品高清 | 不用播放器的免费av| 国产成人精品一区二区免费看京| 午夜精品久久久久久久男人的天堂| 91国在线视频| 国产精品女人毛片| 手机看片福利日韩| 四虎5151久久欧美毛片| 久久免费观看视频| 免费观看黄色一级视频| 一区二区三区中文免费| 污污的视频免费观看| 久久一区二区三区电影| 国产精品欧美日韩| 搞黄视频免费在线观看| 色狠狠一区二区| 30一40一50老女人毛片| 宅男噜噜噜66国产日韩在线观看| 国产精品视频免费观看| 青春草视频在线观看| 日韩精品一区二区三区中文不卡 | 伊人久久综合| 成人黄视频免费| 婷婷在线播放| 欧美v日韩v国产v| 激情五月婷婷小说| 国产黄色精品网站| 亚洲色成人www永久在线观看 | 欧美tk—视频vk| 久久免费播放视频| 成人黄色网址在线观看| 人妻av中文系列| 久久丝袜视频| 欧美在线不卡区| 黄色av网址在线免费观看| 日本丰满少妇一区二区三区| 69精品无码成人久久久久久| 日本欧美一区二区三区| 正在播放一区| 操欧美女人视频| 欧美自拍大量在线观看| www亚洲人| 欧美精品丝袜久久久中文字幕| 中文字幕影音先锋| 99九九99九九九视频精品| 欧美 日韩 国产一区| 青青草成人影院| 亚洲综合日韩中文字幕v在线| 密臀av在线| 亚洲欧美日韩一区二区在线| 中文字幕有码无码人妻av蜜桃| 春色校园综合激情亚洲| 午夜不卡在线视频| www.黄色在线| 国产自产v一区二区三区c| 国产一区二区三区乱码| 免费成人高清在线视频theav| 国产精品av在线播放| 国产三区在线观看| 亚洲国产一区二区三区四区| 涩涩视频在线观看| 一区二区三区波多野结衣在线观看| 成人免费无码大片a毛片| 日韩电影在线一区二区| 亚洲乱码日产精品bd在线观看| 婷婷激情久久| 91免费看国产| 成人av三级| 欧美剧在线观看| 色av男人的天堂免费在线| 337p亚洲精品色噜噜噜| 国产黄色片免费看| 亚洲欧美视频在线观看视频| 大地资源二中文在线影视观看| 精品一区精品二区高清| 免费无码毛片一区二三区| 日韩成人三级| 精品一卡二卡三卡四卡日本乱码| 色狠狠一区二区三区| 97在线视频观看| 菠萝蜜视频国产在线播放| 亚洲黄色www| 国产不卡精品视频| 欧美制服丝袜第一页| 国产精品a成v人在线播放| 国产精品视频线看| 亚洲天堂成人av| 国产精品一区二区三区乱码| 国产精品入口免费软件| 999亚洲国产精| 国产免费裸体视频| 99久久精品网| 色综合电影网| 亚洲人和日本人hd| 国产精品午夜av在线| 成人综合日日夜夜| 国产精品网红福利| 欧美大片免费| 欧美一区在线直播| 欧美14一18处毛片| 欧美成人免费在线观看| 91啦中文在线| 一本色道久久88精品综合| 天天干免费视频| 精品国产第一区二区三区观看体验 | 亚洲视频电影在线| 亚洲精蜜桃久在线| 国产永久精品大片wwwapp| 久久免费看av| 首页亚洲中字| 精品久久久久久亚洲| 果冻天美麻豆一区二区国产| 超碰97人人在线| 香蕉成人app| 成人18视频| jizz国产精品| 国产伦精品一区二区三区四区免费| 国产一区一区| 51国偷自产一区二区三区的来源| 久久亚洲精品中文字幕| 国产精品揄拍500视频| 草民电影神马电影一区二区| 国产精品福利观看| 免费污视频在线一区| 国产精品久久久久久久久久久久| 成人黄色免费短视频| 国产精品激情av电影在线观看| 午夜日韩成人影院| 国产精品久久97| 外国成人毛片| 亚洲一区二区三区久久| 亚洲视频国产| 国产综合精品一区二区三区| 欧美一级色片| 日韩av不卡在线播放| 99精品电影| 伊人网在线免费| 亚洲精品影视| 青青在线视频观看| 免费一级欧美片在线观看| 日本精品一区在线| 国产99久久精品| 欧美做受喷浆在线观看| 国产无人区一区二区三区| 国产又黄又粗又猛又爽的| 亚洲天天做日日做天天谢日日欢| 国产在线成人精品午夜| 一本色道久久加勒比精品 | 麻豆精品新av中文字幕| 手机在线观看日韩av| 成人av资源在线观看| 国产精品成人一区二区三区电影毛片| 日本一区二区免费在线| 日韩在线中文字幕视频| 午夜av一区二区三区| 波多野结衣视频在线观看| 4438x亚洲最大成人网| 丰满人妻一区二区三区免费视频| 亚洲欧美一区二区三区四区| 日本中文字幕在线视频| 午夜美女久久久久爽久久| 成人精品动漫| 国产精品美女xx| 日本一区二区高清不卡| 国产精品久久久久9999爆乳| 日韩黄色片在线观看| 香蕉网在线视频| 国产午夜精品一区二区三区视频| 欧美偷拍第一页| 色成年激情久久综合| a天堂中文在线观看| 亚洲欧洲在线视频| 18+激情视频在线| 青草成人免费视频| 视频一区日韩| 亚洲一二三区精品| 亚洲制服少妇| 亚洲丝袜在线观看| 国产精品久久午夜| 国内自拍视频在线播放| 欧美一区二区观看视频| 高清av在线| 2019中文字幕在线| 99久久免费精品国产72精品九九 | 欧美三级电影在线观看| 狠狠综合久久av一区二区| 日韩在线视频二区| 性欧美videohd高精| 久久av一区二区三区漫画| 一区二区日韩欧美| 久久这里只精品| 久久久国产综合精品女国产盗摄| 国产 日韩 欧美 成人| 6080午夜不卡| 91在线观看| 欧美自拍视频在线| 天海翼亚洲一区二区三区| www.亚洲成人网| 国产综合久久久久久久久久久久| 国产伦精品一区二区三区视频女| 精品动漫一区二区| 亚洲美女综合网| 欧美成人免费一级人片100| 四虎影视成人精品国库在线观看| 日产精品一线二线三线芒果| 久久国产免费| www.久久国产| 欧美午夜精品久久久久久久| 手机看片福利永久| 久久久中精品2020中文| 香蕉大人久久国产成人av| 日韩视频一二三| 国产精品一二二区| 久久免费在线观看视频| 精品99999| 韩国精品一区| 久中文字幕一区| 午夜亚洲福利在线老司机| 欧美无人区码suv| 欧美日韩激情视频| 日韩电影网址| 日本午夜人人精品| 欧洲三级视频| 亚洲美女性囗交| 亚洲欧美激情小说另类| 亚洲不卡免费视频| 国内精品久久久久影院优| 久久男人av| 成人中文字幕av| 国产精品嫩草影院com| 国产精品嫩草影院精东| 久久国产精品99国产精| 日韩一区二区三区精品 | 国产丝袜精品丝袜| 久久久久免费网| 美日韩一区二区三区| 侵犯稚嫩小箩莉h文系列小说| 日韩欧美www| 高清在线视频不卡| 日本精品国语自产拍在线观看| 青草国产精品久久久久久| 国产极品美女在线| 精品国产91亚洲一区二区三区婷婷| 两个人看的在线视频www| 欧美综合激情| 精品午夜一区二区三区在线观看| 欧洲猛交xxxx乱大交3| 亚洲精品国产精品国自产在线| xxxxxx欧美| 中文字幕在线亚洲三区| 盗摄精品av一区二区三区| 天堂а√在线中文在线新版| 最新国产成人av网站网址麻豆| 亚洲不卡在线| 欧美在线观看成人| 亚洲欧洲国产专区| 韩国av免费在线| 国产精品成人aaaaa网站| 欧美日韩亚洲一区三区| 亚洲天堂网一区二区| 欧美日韩亚洲不卡| av小说在线播放| 神马影院午夜我不卡| 国产成人综合网| 精品视频一二三区| 欧美激情久久久久久| 欧美日韩在线二区| 国产+高潮+白浆+无码| 欧美日韩国产a| 亚洲啊v在线|