高并发缓存的高频并发处理

7
我正在学习缓存,对缓存的并发性有疑问。
据我所知,LRU缓存是使用双向链表+哈希表实现的。那么,LRU缓存如何处理高频并发?请注意,从缓存中获取数据和将数据放入缓存中都会更新链表和哈希表,因此缓存一直在修改。
如果我们使用互斥锁来实现线程安全,如果访问量很大,速度会变慢吗?如果不使用锁定,使用哪些技术?提前致谢。

是的,你说得完全正确。在高并发环境中,如果锁必须保持一段时间,监视器锁定将具有显著的性能限制。在这种情况下,您可能会对基于原子操作(如putIfAbsent)开发的并发缓存感兴趣。然而,这是一种复杂的方法,最好的选择是使用并发库,如果可以适应的话。Brian Goetz在《Java并发编程实战》中开发了一个基本的并发缓存。请参考此链接:http://stackoverflow.com/questions/16484939/concurrent-cache-in-java。 - scottb
1个回答

12
传统的LRU缓存由于硬件限制,以及命中惩罚远小于未命中惩罚(例如数据库查找),因此并不适用于高并发场景。对于大多数应用程序而言,如果只用于更新底层结构(而非在未命中时计算值),则锁定缓存是可接受的。当锁变得有争议时,像分段LRU策略这样的简单技术通常已足够好。

使LRU缓存扩展的方法是避免在每次访问时更新策略。关键观察结果表明,缓存的使用者并不关心当前的LRU排序。调用者唯一关心的是缓存维护一个阈值大小和高命中率。这为通过避免在每次读取时改变LRU策略进行优化打开了门。

memcached采用的方法是在一个时间窗口内丢弃后续读取,例如1秒钟。预期该缓存非常大,因此通过这种简单的LRU方法很少有机会驱逐较差的候选项。

ConcurrentLinkedHashMap(CLHM)和随后的Guava's Cache采用的方法是记录缓冲区中的访问。在LRU锁定下,使用try-lock,无需阻塞其他操作即可清空此缓冲区。 CLHM使用多个环形缓冲区,如果缓存无法跟上,则会丢失事件,因为丢失事件比性能下降更可取。 Ehcacheredis采用的方法是概率LRU策略。读取将更新条目的时间戳,并写入以迭代缓存以获取随机样本。从该样本中驱逐最旧的条目。如果该样本构建快速且缓存较大,则被驱逐的条目可能是一个好的候选项。
可能还有其他技术和伪LRU策略(如CLOCK),可以在较低的命中率下提供更好的并发性。

@ Ben, dbf, scottb:我已经阅读了由Ben Manes和Charles Fry提出的concurrentlinkedhashmap,来源于https://code.google.com/p/concurrentlinkedhashmap/wiki/Design。这是一篇非常好的文章,有着聪明的想法和清晰的解释。我还阅读了在文章中提到的LIRS。现在我对缓存的工作原理有了更深入的了解。感谢大家。 - user389955
还可以查看Java 8重写的设计,其中添加了优化。 - Ben Manes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接