一个应用程序中有太多的咖啡因缓存实例会有多少?

huangapple go评论73阅读模式
英文:

How many Caffeine Cache instances in an application is too much?

问题

我有一个使用情况,我想要针对字符串键缓存一组元素,其中地图中的每个元素都可以具有自己的到期时间。我打算使用缓存的缓存,并利用 Caffeine 中的非常酷的可变到期功能。

因此类似于:

Cache<String, Cache<String,ObjectWithVariableExpiry>>

现在,内部缓存应动态创建,父缓存可以有成千上万个条目。我想知道是否可以这样做,或者这是否是对 Caffeine 的非常糟糕的使用。我担心对于每个内部的 Cache<String, ObjectWithVariableExpiry>,计时器线程/逻辑可能会成为资源瓶颈。

非常感谢任何建议。

英文:

I have a use case where I want to Cache a Map of elements against String keys where each element in the map can have its own expiry. I was planning to use a Cache of Caches and utilize the really cool variable expiry in Caffeine.

So something like.

Cache<String, Cache<String, ObjectWithVariableExpiry>>

Now, the internal Cache is supposed to be dynamically created and the parent cache can have thousands of entries. I'm wondering if this is ok to do or if it's a really bad use of Caffeine. My worry is that for each internal Cache<String, ObjectWithVariableExpiry> the timer threads/logic could become a resource hog.

Any suggestions are greatly appreciated.

答案1

得分: 2

我想,如果没有进行分析以查看对堆、对象生成、增长率等的影响,就没有办法回答关于“太多”的问题。

是否存在需要嵌套缓存的行为,或者一个带有复合键的单一缓存是否足够?这将具有相同数量的条目和可变过期时间,但避免了新缓存实例的开销。通常嵌套是为了在组周围执行操作,例如针对特定客户的缓存并使其所有条目失效。如果是这种情况,有一些替代方案,比如在键中添加生成的 ID,从而允许不检索和惰性驱逐旧的生成。内部数据结构为摊销的 O(1),因此条目数量对性能影响较小。

缓存实例的开销是内存,因为缓存不会创建自己的线程。缓存由ConcurrentHashMap支持,使用多个环形缓冲区、用于可变过期的Timing Wheel、填充以防止伪共享,以及如果大小受限则使用CountMin sketch。这使得缓存成为较重的对象,但对于集合来说并不过度。如果为提示过期设置了Scheduler,那么它将为每个缓存实例安排一个单独的定时器。

很可能这不会成为问题。缓存设计用于并发和较长生命周期的用途。这意味着它对于非并发情况下的高实例创建情况不太理想,比如仅限于HTTP请求的范围内。它肯定可以正常工作,但与较简单的数据结构相比,会给垃圾收集器增加更多压力。

很遗憾,从问题中没有足够的信息来给出一个好的答案。很可能问题不大,如果有负面影响,则可能有简单的解决方案,负载测试可能会提供更强的信心。

英文:

I suppose there is no answer as to "too much" without profiling to see the impact on the heap, object churn, growth rate, etc.

Is there behavior requiring the nested caching or could a single cache with a composite key suffice? This would have the same number of entries and variable expiration, but avoid the overhead of new cache instances. Typically nesting is to perform an operation around the group, e.g. customer-specific cache and invalidate all of their entries. If that's the case, there are alternatives like adding a generational id to the key, thereby allowing older generations to not be retrieved and evicted lazily. The internal data structures are amortized O(1) so the number of entries has a small impact on performance.

The overhead of the cache instances is memory, as the cache does not create its own threads. The cache is backed by a ConcurrentHashMap, uses multiple ring buffers, a Timing Wheel for variable expiration, padding to protect against false sharing, and a CountMin sketch if size bounded. This makes the cache a heavier objects, but not excessive for a collection. If setting a Scheduler for prompt expiration, then it will schedule a single timer per cache instance.

Most likely it won't be a problem. The cache is designed towards concurrency and longer lived usages. That means it isn't as optimal for non-concurrent cases with high instance creation, like a scoped to an http request. It would certainly work fine, but add more pressure to the garbage collector compared to a simpler data structure.

Unfortunately from the question there isn't enough to give a good answer. It is probably okay, you might have simple solutions if there is a negative effect, and a load test might provide stronger confidence.

huangapple
  • 本文由 发表于 2020年8月19日 03:01:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/63475070.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定