日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2025/3/21 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 go语言高性能缓存组件ccache分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 背景

在擼代碼時,利用局部性原理對數據做緩存是一種常用的性能優化手段。

要做緩存,離不開的就是緩存組件。ccache就是一個很優秀的lru緩存組件,其做了很多很巧妙的優化策略來降低鎖沖突,實現高性能。

降低鎖沖突的策略有

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

下面看下具體是怎么實現的。

2. lru cache

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

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

要實現lru策略,一般是用hashtable和list數據結構來實現,hashtable支持通過key快速檢索到對應的value,list用來記錄元素的訪問時間序,支持淘汰最久未訪問的內容。如下圖

在hashtable中,key對應的內容包含兩部分,第一部分為實際要存儲的內容,這里定義為value,第二部分是一個指針,指向對應在list中的節點,將其定義為element。

在list中,每個節點也包含兩個部分,第一個部分是一個指針,指向hashtable中對應的value,這里定義為node,第二部分是next指針,用來串起來整個鏈表。

若我們執行get(key2)操作,會先通過key2找到value2和element2,通過element2又能找到node2,然后將node2移動到list隊首,所以執行完get(key2)后,上圖會變為

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

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

3. ccache優化策略

針對上面的問題,ccache采用了下面幾種優化策略,都非常的巧妙。

3.1 對hashtable做分片

這是個很常見的策略。

將一個hashtable根據key拆分成多個hashtable,每個hashtable對應一個鎖,鎖粒度更細,沖突的概率也就更低了。

如圖所示,一個hashtable根據key拆分成三個hashtable,鎖也變成了三個。這樣當并發訪問hashtable1和hashtable2時,就不會沖突了。

3.2 累計訪問多次才做提權

value中新增一個訪問計數,每次get操作時,計數+1。當計數達到閾值時,才將其移動到list的隊首,同時將計數重置為0。

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

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

3.3 單開一個線程更新list

在get和set操作時,都需要更新記錄訪問時間序的list,但更新操作只需要在下次set操作前完成就可以,并不需要實時更新。基于這一點,可以單獨開一個更新線程對list做更新。get和set時,提交更新任務到隊列中,更新線程不停從隊列中取任務做更新。

這樣做有兩個好處

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

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

3.4 批量淘汰

當緩存滿了后,一次淘汰一批元素。優化在緩存滿了的時候,每次set新元素都會觸發淘汰的問題。

3.5 整體流程

在實現完上述策略后,整體流程大致是這樣的

3.5.1 get操作

3.5.2 set操作

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。