go语言api源码中文版_Go语言学习——sync.map源码剖析
1.簡介
最近看了下Sync包,詳讀了sync.map源碼,感覺源碼實(shí)現(xiàn)還是比較巧妙的,有不少可以學(xué)習(xí)的地方;在講源碼前,先看下sync.map的"歷史",從網(wǎng)上搜資料,sync.map是Go語言在1.9版本才引入的并發(fā)安全的map,對此,有些同學(xué)心中可能會有個(gè)疑問,如果是支持并發(fā),為什么不采取鎖map的方式,為啥還要在單獨(dú)搞個(gè)sync.map結(jié)構(gòu)呢?我們先看下鎖map存在的問題:
參考:go語言中文文檔:www.topgoer.com
轉(zhuǎn)自:https://studygolang.com/topics/12363#reply0
1)mutex + map
最簡單的方案就是在map上加個(gè)鎖,針對map的所有操作都要提前加鎖,其存在問題也很明顯,鎖競爭會非常頻繁;
2)rwmutex + map
優(yōu)化一點(diǎn),依據(jù)場景,如果是讀操作多于寫操作,可以把mutex換成rwmutex,相比方案一,有一定優(yōu)化、至少讀讀之間不會存在互斥,不過,讀寫之間還會存在阻塞;
根據(jù)鎖map的優(yōu)化迭代方案可知,在讀讀場景下,rwmutex + map可以并發(fā)、不存在阻塞,但是,讀寫還是存在阻塞,而sync.map要做的事情就是能進(jìn)一步優(yōu)化:對于map的各種操作,盡可能不阻塞;為此,sync.map采用了兩級緩存實(shí)現(xiàn),一級緩存做無鎖并發(fā),二級緩存做有鎖并發(fā),如:
對上圖說明兩點(diǎn):
1)針對sync.map的各種操作,都先經(jīng)過一級緩存,一級緩存采用無鎖的方式,只要不出現(xiàn)擊穿,即key都在一級緩存中可以找到,則就不會訪問到二級緩存;
2)一級緩存和二級緩存之間存在數(shù)據(jù)同步,二級緩存數(shù)據(jù)相對更全一些,所以當(dāng)一級緩存數(shù)據(jù)比較久時(shí),可以將二級緩存數(shù)據(jù)同步一下,該情況是在讀擊穿時(shí)處理;在不擊穿的前提下,一級緩存中可能有數(shù)據(jù)刪除,數(shù)據(jù)移除情況也要同步給二級緩存,清除廢棄數(shù)據(jù)、減少空間占用,該情況是在寫擊穿并且是一、二級緩存都不存在鍵的情況處理,總之,同步的原則是:一級緩存數(shù)據(jù)盡可能新;一級緩存數(shù)據(jù)只能是二級緩存的子集;
2.實(shí)現(xiàn)
sync.map的優(yōu)勢是理想情況下以無鎖代替有鎖、提高性能,但存在擊穿后不得不加鎖的問題,一旦擊穿進(jìn)入二級緩存,就要進(jìn)行鎖操作了,所以sync.map不太適用于寫多讀少以及頻繁創(chuàng)建新鍵的情況;因?yàn)橐紤]擊穿問題,所以sync.map的實(shí)現(xiàn)也是圍繞擊穿考慮的。 2.1讀操作
讀操作比較簡單,步驟是:
1)查看一級緩存中是否有key,有就返回對應(yīng)value;
2)如果沒有則進(jìn)入讀擊穿,加鎖后,在復(fù)看一級緩存中是否有key(復(fù)看是因?yàn)榇嬖诙壘彺嫦蛞患壘彺嫱綌?shù)據(jù)的情況),有就返回對應(yīng)value;
3)如果沒有則看二級緩存中有沒有,有就返回對應(yīng)value,此時(shí)出現(xiàn)讀擊穿,會進(jìn)入讀擊穿保護(hù)機(jī)制——擊穿達(dá)到一定次數(shù),會將二級緩存數(shù)據(jù)同步到一級緩存;
需要注意的是,在返回value時(shí)要檢測value的有效性,如果已經(jīng)廢棄(expunged狀態(tài)),則不用返回。
2.2寫操作
寫操作相對復(fù)雜,根據(jù)key是否存在的情況,可以分為create和update,步驟是:
1)查看一級緩存中是否有key,有就嘗試更新,之所以是嘗試是因?yàn)檫€要檢查數(shù)據(jù)是否已經(jīng)廢棄,如果已經(jīng)廢棄,即使key在一級緩存中存在,也是擊穿效果,因?yàn)槎壘彺嬷袥]有;
2)如果一級緩存操作失敗,加鎖后,在復(fù)看一級緩存,如果有key,則更新value,并檢測value是否為廢棄狀態(tài),如果是,則將key、value寫入二級緩存;
3)如果一級緩存中一直沒有key,但二級緩存中有,則直接更新數(shù)據(jù);
4)如果一級緩存和二級緩存都沒有key,則將key、value寫入二級緩存,此時(shí)會嘗試將一級緩存數(shù)據(jù)同步給二級緩存,用于刪除廢棄數(shù)據(jù)(將一級緩存中的刪除數(shù)據(jù)設(shè)置為expunged狀態(tài)),因?yàn)橹挥性撉闆r下,一級緩存數(shù)據(jù)可能是二級緩存數(shù)據(jù)的子集,所以當(dāng)插入全新的key時(shí),才會嘗試更新緩存數(shù)據(jù)、移除廢棄數(shù)據(jù);
2.3刪除操作
刪除采取的是延遲刪除操作,對于待刪除數(shù)據(jù),其value先設(shè)置為nil,優(yōu)先從一級緩存刪除,如果一級緩存沒有,再去二級緩存中刪除。
2.4源碼
以1.14.4版本為例,處理源碼是:
3.總結(jié)
sync.map是以無鎖操作一級緩存的方式支持并發(fā)、提高性能,而根據(jù)其實(shí)現(xiàn)可知,sync.map適用于讀多、更新多、新建少的場景(新建情況下,可能會帶來較大的開銷,比如:讀擊穿、數(shù)據(jù)剛從二級緩存同步到一級緩存后,又要新建key,數(shù)據(jù)又要反向同步一次)。
總結(jié)
以上是生活随笔為你收集整理的go语言api源码中文版_Go语言学习——sync.map源码剖析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数字格式化
- 下一篇: 怎么成为日上会员直邮_18个日上直邮问题