Go中的緩存現(xiàn)狀(BigCache&FreeCache&GroupCache 緩存框架對比)

Go中的緩存現(xiàn)狀

這篇文章登上了Golang 在Reddit subreddit板塊的頂部,并在Hacker News 首頁排名到第二名。歡迎各位來閱讀討論,并在Github上面給我們一個小星星。

每個數(shù)據(jù)庫都需要一個智能的緩存系統(tǒng)。緩存需要保存最近最頻繁訪問的內(nèi)容,并且支持配置一些限制上的配置。

作為一個圖形數(shù)據(jù)庫,Dgraph可以在每次查詢中,訪問數(shù)千甚至數(shù)百萬的key。這個功能主要依賴于他中間結果的數(shù)量。由于通過鍵值對訪問數(shù)據(jù)庫會導致磁盤上的查詢操作,出于對性能方面的考慮(磁盤訪問速度不及內(nèi)存),我們希望優(yōu)化這塊的性能。

通常的訪問模式都遵循 ZipFian分布,訪問頻率最高的key,比其他的key訪問次數(shù)要多很多。從Dgraph中也能看到這一點(熱點Key的問題)。

我們非常高興能用Go語言來實現(xiàn)我們的Dgraph 組件,關于為什么Go語言適合做后端開發(fā),這個內(nèi)容太多了,在這里不贅述了。盡管Go的生態(tài)還不夠健全,但不能否認Go是一個很不錯的編程語言,而且我們也不會用別的語言來替代Go。

關于Go生態(tài)缺失的怨言隨處可見。但是我覺得Go是成熟的,他已經(jīng)實現(xiàn)了對機器內(nèi)核的快速編譯,執(zhí)行和利用內(nèi)核完成工作。但是作為一個致力于構建高并發(fā)的編程語言,對于性能上仍然有一些缺陷,并發(fā)庫可以很好地擴展內(nèi)核數(shù)量。對于并發(fā)的數(shù)組和字典,用戶可以自由的使用和練習。對于串行語言來說,這樣是合理的,但是對于以并行構建的編程語言,這點上似乎有一些缺陷。

特別的是,Go缺少并發(fā)的LRU/LFU 緩存,這兩者可以很好地擴展到全局緩存中。在這片博客里面,我會帶你一起來了解一下通常情況下的各種處理方式,包括在我的的Dgraph中進行的一些測試。Aman 同時也會展示一些目前Go生態(tài)中的設計理念,性能,命令率等的一些實踐內(nèi)容。

緩存框架的必備需求

  1. 并發(fā)
  2. 內(nèi)存限制(限制最大的可使用空間)
  3. 在多核和多goroutines之間更好的擴展
  4. 在非隨機密鑰的情況下,很好地擴展(eg. Zipf)
  5. 更高的緩存命中率

Go map 與 sync.Mutex的結合使用

Go map 結合 sync.Mutex 是應對緩存的常見形式(獨占所)。但這也確實會導致所有的Goroutines同時在一個地方鎖住, 產(chǎn)生嚴重的鎖競爭問題。而且也不能對內(nèi)存的使用量做限制。所以對于有內(nèi)存限制要求的場景,這個方案不適用。

不滿足 上面的2,3,4條

Go maps 與 lock striping

這個方式的原理與上面的一樣,但是鎖的粒度更小(詳見這里),很多程序員錯誤的認為,降低鎖的粒度可以很好地避免競爭,特別是在分片數(shù)超過程序的線程數(shù)時(GOMAXPROCS)

在我們嘗試編寫一個簡單的內(nèi)存限制緩存的時候,我們也是這樣做的。為了保證內(nèi)存可以在釋放之后還給操作系統(tǒng)。我們定期掃描我們的分片,然后釋放掉創(chuàng)建的map,方便以后被再次使用。這種粗淺的方式卻很有效,并且性能優(yōu)于LRU(后面會解釋),但是也有很多不足。

  1. Go請求內(nèi)存很容易,但釋放給操作系統(tǒng)卻很難。當碎片被清空的同時,goroutines去訪問key的時候,會開始分配內(nèi)存空間,此時之前的內(nèi)存空間并沒有被完全釋放,這導致內(nèi)存的激增,甚至會出發(fā)OOM錯誤。
  2. 我們沒有意識到,訪問的模式還受Zipf定律的束縛。最常訪問的幾個key仍然存在幾個鎖,因此產(chǎn)生goroutines的競爭問題。這種方式不滿足多核之間的擴展的需求。

不滿足 上面的2,4條

LRU 緩存

Go 里面,groupcache 實現(xiàn)了一個基本的LRU 緩存,在通過lock striping實現(xiàn)失敗之后,我們通過引入lock的方式優(yōu)化了LRU的這部分內(nèi)容,使它支持了并發(fā)。雖然這樣解決了上面描述的內(nèi)存激增的問題,但是我們意識到他同樣地會引入競爭的問題。

這個緩存的大小同樣也依賴于緩存的條數(shù),而不是他們消耗的內(nèi)存量。在Go的堆上面計算復雜的數(shù)據(jù)結構所消耗的內(nèi)存大小是非常麻煩的,幾乎不可能實現(xiàn)。我們嘗試了很多方式,但是都無法奏效。緩存被放入之后,大小也在不停地變化(我們計劃之后避免這種情況)

我們無法預估緩存會引起多少的競爭。在使用了近一年的情況下,我們意識到緩存上面的競爭有多嚴重,刪除掉這塊之后,我們的緩存效率提高了10倍。

在這塊的實現(xiàn)上,每次讀取緩存會更新鏈表中的相對位置。因此每個訪問都在等待一個互斥鎖。此外LRU的速度比Map要慢,而且在反復的進行指針的釋放,維護一個map和一個雙向鏈表。盡管我們在惰性加載上面不斷地優(yōu)化,但依然遭受到競爭的而影響。

不滿足3,4

分片LRU 緩存

我們沒有實際的去嘗試,但是依據(jù)我們的經(jīng)驗,這只會是一個暫時的解決方法,而且并不能很好地擴展。(不過在下面的測試里面,我們依然實現(xiàn)了這個解決方案)

不滿足4

流行的緩存實現(xiàn)方式

許多方法的優(yōu)化點是節(jié)省GC在map碎片上花費的時間。GC的時間會隨著map存數(shù)數(shù)量的增加而增大。減少的方案就是分配更少的數(shù)量,單位空間更大的區(qū)域,在每個空間上存儲更多的內(nèi)容。這確實是一個有效地方法,我們在Badger里面大量的使用了這個方法(Skiplist,Table builder 等)。 很多Go流行的緩存框架也是這么做的。

BigCache的緩存

BigCache會通過Hash的方式進行分片。 每個分片都包含一個map和一個ring buffer。無論如何添加元素,都會將它放置在對應的ring buffer中,并將位置保存在map中。如果多次設置相同的元素,則ring buffer中的舊值則會被標記為無效,如果ring buffer太小,則會進行擴容。

每個map的key都是一個uint32的 hash值,每個值對應一個存儲著元數(shù)據(jù)的ring buffer。如果hash值碰撞了,BigCache會忽略舊key,然后把新的值存儲到map中。預先分配更少,更大的ring buffer,使用map [uint32] uint32是避免支付GC掃描成本的好方法

FreeCache

FreeCache 將緩存分成了256段,每段包括256個槽和一個ring buffer存儲數(shù)據(jù)。當一個新的元素被添加進來的時候,使用hash值下8位作為標識id,通過使用LSB 9-16的值作為槽ID。將數(shù)據(jù)分配到多個槽里面,有助于優(yōu)化查詢的時間(分治策略)。

數(shù)據(jù)被存儲在ring buffer中,位置被保存在一個排序的數(shù)組里面。如果ring buffer 內(nèi)存不足,則會利用LRU的策略在ring buffer逐個掃描,如果緩存的最后訪問時間小于平均訪問的時間,就會被刪掉。要找到一個緩存內(nèi)容,在槽中是通過二分查找法對一個已經(jīng)排好的數(shù)據(jù)進行查詢。

GroupCache

GroupCache使用鏈表和Map實現(xiàn)了一個精準的LRU刪除策略的緩存。為了進行公平的比較,我們在GroupCache的基礎上,實現(xiàn)了一個包括256個分片的切片結構。

性能對比

為了比較各種緩存的性能,我們生成了一個zipf分布式工作負載,并使用n1-highcpu-32機器運行基準測試。下表比較了三個緩存庫在只讀工作負載下的性能。

只讀情況

我們可以看到,由于讀鎖是無消耗的,所以BigCache的伸縮性更好。FreeCache和GroupCache讀鎖是有消耗的,并且在并發(fā)數(shù)達到20的時候,伸縮性下降了。(Y軸越大越好)

只寫情況

在只寫的情況下,三者的性能表現(xiàn)比較接近,F(xiàn)reeCache比另兩個的情況,稍微好一點。

讀寫情況(25% 寫,75%讀)

兩者混合的情況下,BigCache看起來是唯一一個在伸縮性上表現(xiàn)完美的,正如下一節(jié)所解釋的那樣,命中率對于Zipf工作負載是不利的。

命中率比較

下面的表格中展示了三個框架的命中率。FreeCache非常接近GroupCache實現(xiàn)的LRU策略。然而,BigCache在zipf分布式工作負載下表現(xiàn)不佳,原因如下:

  • BigCache不能有效地利用緩沖區(qū),并且可能會在緩沖區(qū)中為同一個鍵存儲多個條目。
  • BigCache不更新訪問(讀)條目,因此會導致最近訪問的鍵被刪除。
CACHE SIZE (# OF ELEM) 10000 100000 1000000 10000000
BigCache - 37% 52% 55%
FreeCache - 38% 55% 90%
GroupCache 29% 40% 54% 90%

所以說,沒有哪個框架能滿足所有緩存的需求。

那還有什么沒說的么?

其實也沒什么了,Go中并沒有一個能滿足所有場景的智能緩存框架,如果你發(fā)現(xiàn)了有這種,請快快聯(lián)系我。

與此同時,我們遇到了Caffeine,一個Java的庫,被用于使用Cassandra, Finagle 和一些其他的數(shù)據(jù)庫系統(tǒng)。他使用的是TinyLFU,一個高效的緩存接納策略,并使用各種技術來擴展和執(zhí)行,隨著線程和內(nèi)核數(shù)量的增長,同時提供接近最佳命中率。您可以在這篇文章中了解它是如何工作的。

Caffeine 滿足了我開始提到的所有的5個需求,所以我正在考慮構建一個Go版本的Caffeine。他不僅能滿足我們的需求,同事也可能填補Go語言中并發(fā),高性能,內(nèi)存限制的緩存框架的空白。如果你也想?yún)⑴c或者你已經(jīng)有類似的成果了,請聯(lián)系我。

感謝

我們想要感謝 Benjamin Manes 幫助我們對Caffeine進行一些Go版本的性能測試(Code here),我們還要感謝Damian Gryski為我們提供了基準緩存命中率的基本框架(這里),我們還修改了它,來滿足我們的需要。他已經(jīng)接受了我們對于他代碼庫(GitHub)的修改。

感謝閱讀,如果方便的話,給我們Github 點個星星吧。

via:

翻譯

原文鏈接:The State of Caching in Go
作者:Manish Rai Jain
譯者:JYSDeveloper

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內(nèi)容

  • 紅塵中我們只是偶爾想起,紛繁的俗世中真心實意走了一回,不要說什么愛與不愛,我只是努力的活著已經(jīng)很累。 ...
    榮一心_d442閱讀 453評論 5 13
  • 借著充滿悲嗆的喉 舉起用往事釀的酒 敬一個不會回頭的你
    花敗不開閱讀 254評論 0 1
  • “南山南,北秋悲,南山有谷堆;南風喃,北海北,北海有墓碑。”初聞北海,是在《南山南》這首悲涼感人的民謠里。北海,一...
    娟娟新月閱讀 3,952評論 110 269
  • 1、后海散步打卡,丁丁陪伴,和狗狗玩耍,呼吸新鮮空氣,鏈接大自然,體驗多維世界,打開自己的各種感官,不斷提升身體能...
    張艾雯閱讀 308評論 2 2