Ehcache 是否使用后台线程来删除过期项目?

问题描述

我此时正在使用 guava 缓存,并考虑迁移到 Ehcache。根据这个 post 没有线程确保在番石榴缓存中的延迟过去后立即从缓存中删除条目:

使用 CacheBuilder 构建的缓存不执行清理和驱逐值 “自动”,或在值过期后立即,或任何 那种。相反,它在运行期间执行少量维护 写操作,或者在偶尔的读操作期间,如果写是 罕见。

这样做的原因如下:如果我们要执行Cache 持续维护,我们需要创建一个线程,它的 操作将与用户操作竞争共享锁。 此外,一些环境限制线程的创建, 这将使 CacheBuilder 在该环境中无法使用。

但是 Ehcache 呢?是否有单独的线程在使用内存存储时删除过期项目?我试图寻找信息,但有人说没有,有人说有。

解决方法

TLDR: Ehcache 不提供提示过期。 Guava 清理更迅速,但需要外部调度以保证粗粒度。 Caffeine 可以根据下一个到期事件安排清理。

Ehcache 依靠大小驱逐来丢弃过期的条目。这意味着当需要空间时,尽管缓存中保存了许多过期的死条目,但仍可能会丢弃一个活动条目。由于 Ehcache 使用统一采样来发现驱逐候选,因此它必须偶然发现一个死条目才能考虑将其删除。

在审查 Ehcache 2.x 和 3.x 的实现时,我的理解是两者都更喜欢驱逐过期条目而不是实时条目。采样条目按其 lastAccessTime 字段排序,以便驱逐最近最少使用的条目。如果到期时间不同,例如共享缓存同时包含短期 2FA 令牌和长期数据,那么在等待过期条目老化时可能会浪费大量空间。这种方法反映了 Memcached 和 Redis,但是由于它们非常大,这些缓存使用后台线程来主动查找和删除死条目。

Guava 在 O(1) 优先级队列中维护条目,以便它可以在维护期间删除过期条目,例如在可能需要驱逐的写入之后。这仅实现了固定到期时间,因此可以将条目存储在双向链表中并在恒定时间内重新排序到尾部位置。通过查看列表的头部,Guava 可以立即找到并修剪过期的条目。正如@LouisWasserman 建议的那样,定期调用 Cache.cleanUp() 可确保非活动缓存将检查列表并丢弃过期条目。无论如何,缓存将定期执行维护以响应读取和写入活动。

Caffeine 通过支持 per-entry expirationscheduler 来扩展这种方法,以根据下一个到期事件触发缓存维护。使用 hierarchical timer wheel 在 O(1) 时间内实现变量过期。如果提供了调度线程,则调度任务将执行维护并导致过期条目立即被逐出。在 Java 9+ 中,JVM 包含一个专用的调度线程,如果配置了 Scheduler.systemScheduler(),Caffeine 可以使用该线程。

LoadingCache<Key,Graph> graphs = Caffeine.newBuilder()
    .scheduler(Scheduler.systemScheduler())
    .expireAfterWrite(10,TimeUnit.MINUTES)
    .build(key -> createExpensiveGraph(key));

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...