2013-09-23 21 views
6

我正在學習緩存並對緩存的併發性有疑問。緩存的高頻併發性

據我所知,LRU緩存是用雙鏈表+散列表實現的。那麼LRU緩存如何處理高頻併發?請注意,從緩存中獲取數據並將數據放入緩存將更新鏈接列表和哈希表,以便緩存一直進行修改。

如果我們爲線程安全使用互斥鎖,如果緩存被大量人員訪問,速度是否會減慢?如果我們不使用鎖,使用什麼技術?提前致謝。

+0

是的,你完全正確。在高度併發的環境中,如果鎖定必須保持相當長的時間,監視器鎖定將會有顯着的性能限制。在這種情況下,您可能有興趣開發基於原子操作(如putIfAbsent)的併發緩存。然而,這是一個複雜的方法,最好的辦法是使用併發庫,如果你可以適應。 Brian Goetz的Java併發實踐中開發了一個基本的併發緩存。請參閱此鏈接:http://stackoverflow.com/questions/16484939/concurrent-cache-in-java。 – scottb

回答

9

傳統的LRU緩存並不是針對高併發性而設計的,因爲硬件有限,命中懲罰遠低於未命中懲罰(例如數據庫查找)。對於大多數應用程序,如果僅用於更新底層結構(不計算未命中的值),則鎖定緩存是可以接受的。分割LRU策略的簡單技術通常足夠好,當鎖被爭奪時。

使LRU緩存擴展的方法是避免更新每個訪問的策略。要做的關鍵觀察是緩存的用戶不關心當前的LRU排序是什麼。調用者唯一關心的問題是緩存保持閾值大小和高命中率。這爲避免在每次讀取時改變LRU策略打開了優化的大門。

memcached採取的方法是丟棄時間窗口內的後續讀取,例如, 1秒。緩存預計會非常大,因此通過這個更簡單的LRU驅逐可憐的候選人的可能性非常低。

ConcurrentLinkedHashMap(CLHM)和隨後的Guava's Cache採取的方法是將訪問記錄在緩衝區中。該緩衝區在LRU的鎖定下被排空,並且通過使用try-lock不需要其他操作被阻止。 CLHM使用多個環形緩衝區,如果緩存無法跟上,這些環形緩衝區是有損的,因爲丟失事件比性能下降更受歡迎。

Ehcacheredis採取的方法是概率LRU策略。讀取更新條目的時間戳,並且寫入迭代緩存以獲得隨機樣本。最古老的條目從該樣本中被逐出。如果樣本構建速度快,緩存大,那麼被驅逐的條目可能是一個很好的候選人。

可能還有其他技術,當然,僞LRU策略(如CLOCK)可以在更低的命中率下提供更好的併發性。

+0

@ Ben,dbf,scottb:我已閱讀https://code.google.com/p/concurrentlinkedhashmap/wiki/Design中由Ben Manes和Charles Fry提出的concurrentlinkedhashmap。這是一篇非常好的文章,有一個聰明的想法和明確的解釋。我還閱讀了文章中提到的LIRS。我現在對緩存的工作原理有了更深入的瞭解。謝謝大家。 – user389955

+0

非常好的概述本,謝謝。 – hotzen

+0

另請參閱Java 8 rewrite的[設計](https://github.com/ben-manes/caffeine/wiki/Design),其中增加了優化。 –