日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

go语言高性能缓存组件ccache分析

發(fā)布時(shí)間:2025/3/21 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 go语言高性能缓存组件ccache分析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1. 背景

在擼代碼時(shí),利用局部性原理對(duì)數(shù)據(jù)做緩存是一種常用的性能優(yōu)化手段。

要做緩存,離不開的就是緩存組件。ccache就是一個(gè)很優(yōu)秀的lru緩存組件,其做了很多很巧妙的優(yōu)化策略來降低鎖沖突,實(shí)現(xiàn)高性能。

降低鎖沖突的策略有

  • 一個(gè)元素在累計(jì)被訪問多次后才做提權(quán)(提權(quán)指將元素移動(dòng)到lru鏈的頭部)
  • 將提權(quán)操作放到一個(gè)隊(duì)列中,由一個(gè)單獨(dú)的線程做處理
  • 在同一個(gè)線程中做垃圾回收操作

下面看下具體是怎么實(shí)現(xiàn)的。

2. lru cache

在分析源代碼前,先簡(jiǎn)單了解下lru cache是做什么的。

lru為least recently used的縮寫,顧名思義,lru cache在緩存滿后,再緩存新內(nèi)容需先淘汰最久未訪問的內(nèi)容。

要實(shí)現(xiàn)lru策略,一般是用hashtable和list數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn),hashtable支持通過key快速檢索到對(duì)應(yīng)的value,list用來記錄元素的訪問時(shí)間序,支持淘汰最久未訪問的內(nèi)容。如下圖

在hashtable中,key對(duì)應(yīng)的內(nèi)容包含兩部分,第一部分為實(shí)際要存儲(chǔ)的內(nèi)容,這里定義為value,第二部分是一個(gè)指針,指向?qū)?yīng)在list中的節(jié)點(diǎn),將其定義為element。

在list中,每個(gè)節(jié)點(diǎn)也包含兩個(gè)部分,第一個(gè)部分是一個(gè)指針,指向hashtable中對(duì)應(yīng)的value,這里定義為node,第二部分是next指針,用來串起來整個(gè)鏈表。

若我們執(zhí)行g(shù)et(key2)操作,會(huì)先通過key2找到value2和element2,通過element2又能找到node2,然后將node2移動(dòng)到list隊(duì)首,所以執(zhí)行完get(key2)后,上圖會(huì)變?yōu)?/p>

這時(shí)假如又有一個(gè)set(key5, value5)操作,而我們的cache最多只能緩存4條數(shù)據(jù),會(huì)怎么處理呢。首先會(huì)在hashtable中插入key5和value5,并且在list的隊(duì)首插入node5,然后取出list隊(duì)尾的元素,這里是node4,將其刪除,同時(shí)刪除node4對(duì)應(yīng)的在hashtable中的數(shù)據(jù)。執(zhí)行完上圖會(huì)變成

通過上述流程,可以很好的實(shí)現(xiàn)lru策略。但是因hashtable和list這兩種數(shù)據(jù)結(jié)構(gòu)都不是線程安全的,若要在多線程環(huán)境下使用,無論set操作還是get操作都需要加鎖,這樣就會(huì)很影響性能,特別是現(xiàn)在的服務(wù)器cpu核心數(shù)量越來越多,加鎖對(duì)性能的損耗是非常大的。

3. ccache優(yōu)化策略

針對(duì)上面的問題,ccache采用了下面幾種優(yōu)化策略,都非常的巧妙。

3.1 對(duì)hashtable做分片

這是個(gè)很常見的策略。

將一個(gè)hashtable根據(jù)key拆分成多個(gè)hashtable,每個(gè)hashtable對(duì)應(yīng)一個(gè)鎖,鎖粒度更細(xì),沖突的概率也就更低了。

如圖所示,一個(gè)hashtable根據(jù)key拆分成三個(gè)hashtable,鎖也變成了三個(gè)。這樣當(dāng)并發(fā)訪問hashtable1和hashtable2時(shí),就不會(huì)沖突了。

3.2 累計(jì)訪問多次才做提權(quán)

value中新增一個(gè)訪問計(jì)數(shù),每次get操作時(shí),計(jì)數(shù)+1。當(dāng)計(jì)數(shù)達(dá)到閾值時(shí),才將其移動(dòng)到list的隊(duì)首,同時(shí)將計(jì)數(shù)重置為0。

如閾值是3,那么對(duì)list的寫操作就會(huì)降低3倍,鎖沖突的概率也會(huì)減少3倍。

這是一個(gè)有損的策略,會(huì)使list的順序不完全等同于訪問時(shí)間序。但考慮到lru cache的get操作頻率很高,這種策略對(duì)命中率的損失應(yīng)該是可以忽略的。

3.3 單開一個(gè)線程更新list

在get和set操作時(shí),都需要更新記錄訪問時(shí)間序的list,但更新操作只需要在下次set操作前完成就可以,并不需要實(shí)時(shí)更新。基于這一點(diǎn),可以單獨(dú)開一個(gè)更新線程對(duì)list做更新。get和set時(shí),提交更新任務(wù)到隊(duì)列中,更新線程不停從隊(duì)列中取任務(wù)做更新。

這樣做有兩個(gè)好處

  • list不存在多線程訪問,不需加鎖
  • 操作完hashtable直接返回,異步更新list,函數(shù)相應(yīng)速度更快

這樣會(huì)帶來一個(gè)問題,當(dāng)cpu核心很多,get和set的qps很高時(shí),這個(gè)更新線程可能成為瓶頸。不過考慮到list的操作是非常輕量的,再加上服務(wù)不可能全部資源都放到讀寫cache上,這點(diǎn)也是可以忽略的。

3.4 批量淘汰

當(dāng)緩存滿了后,一次淘汰一批元素。優(yōu)化在緩存滿了的時(shí)候,每次set新元素都會(huì)觸發(fā)淘汰的問題。

3.5 整體流程

在實(shí)現(xiàn)完上述策略后,整體流程大致是這樣的

3.5.1 get操作

3.5.2 set操作

總結(jié)

以上是生活随笔為你收集整理的go语言高性能缓存组件ccache分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。