【问题标题】:High frequent concurrency for cache高速缓存的高并发
【发布时间】:2013-09-23 21:12:09
【问题描述】:

我正在学习缓存,有一个关于缓存并发的问题。

据我所知,LRU缓存是用双链表+哈希表实现的。那么LRU缓存是如何处理高频并发的呢?请注意,从缓存中获取数据和将数据放入缓存都会更新链表和哈希表,因此缓存会一直被修改。

如果我们使用互斥锁来保证线程安全,那么如果大量人访问缓存,速度会不会变慢?如果我们不使用锁,使用什么技术?提前致谢。

【问题讨论】:

  • 是的,你完全正确。在高度并发的环境中,如果锁必须保持很长一段时间,则监视器锁定将具有显着的性能限制。在这种情况下,您可能有兴趣开发基于原子操作(例如 putIfAbsent)的并发缓存。然而,这是一种复杂的方法,最好的办法是使用并发库,如果你可以适应的话。在 Brian Goetz 的 Java Concurrency in Practice 中开发了一个基本的并发缓存。在此处查看此链接:stackoverflow.com/questions/16484939/concurrent-cache-in-java

标签: caching concurrency


【解决方案1】:

传统的 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,来自 code.google.com/p/concurrentlinkedhashmap/wiki/Design。这是一篇非常好的文章,有一个聪明的想法和清晰的解释。我还阅读了文章中提到的LIRS。我现在对缓存的工作原理有了更深入的了解。谢谢大家。
  • 另见 Java 8 重写的 design,它增加了优化。
猜你喜欢
  • 1970-01-01
  • 2017-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-14
  • 1970-01-01
  • 1970-01-01
  • 2014-12-10
相关资源
最近更新 更多