【发布时间】:2016-08-31 12:46:41
【问题描述】:
我知道 Guava Cache 允许为各个缓存配置到期时间。 Guava 是否使用在配置的秒数后唤醒以使缓存无效的计时器来执行此操作?
我有一个长期运行的事务。无论事务开始时缓存中的内容是什么,我都希望它一直持续到事务结束。因此,即使缓存的有效秒数在事务期间过期,从缓存访问的值应该保持不变,直到我们到达事务结束。这在番石榴中可行吗?
谢谢,
亚什
【问题讨论】:
我知道 Guava Cache 允许为各个缓存配置到期时间。 Guava 是否使用在配置的秒数后唤醒以使缓存无效的计时器来执行此操作?
我有一个长期运行的事务。无论事务开始时缓存中的内容是什么,我都希望它一直持续到事务结束。因此,即使缓存的有效秒数在事务期间过期,从缓存访问的值应该保持不变,直到我们到达事务结束。这在番石榴中可行吗?
谢谢,
亚什
【问题讨论】:
来自When Does Cleanup Happen? · CachesExplained · google/guava Wiki:
使用
CacheBuilder构建的缓存不执行清理和驱逐值 “自动”或在值过期后立即,或任何 排序。相反,它在期间执行少量维护 写操作,或者在偶尔的读操作期间,如果写是 很少见。原因如下:如果我们要执行
Cache持续维护,我们需要创建一个线程,它的 操作将与用户操作竞争共享锁。 此外,一些环境限制线程的创建, 这将使CacheBuilder在该环境中无法使用。相反,我们将选择权交给您。如果您的缓存是 高吞吐量,那么您不必担心执行缓存 维护以清理过期条目等。如果你的缓存 很少写入并且您不希望清理以阻止缓存 读取,您可能希望创建自己的维护线程来调用 定期
Cache.cleanUp()。如果您想为某个缓存安排定期缓存维护, 很少有写入,只需安排维护使用
ScheduledExecutorService.
因此,如果您只是在进行阅读,那么如果您在交易之前和之后执行Cache.cleanUp(),那么您“可能”会很好,但仍然不能保证。
但是,与其试图强制项目保留在缓存中,不如使用removalListener 将项目逐出到另一个缓存/映射,然后当您阅读时,您首先需要检查缓存,然后,如果它不存在,请检查在长时间运行的事务中被驱逐的项目。
以下是一个过于简单的例子:
Map<Integer, String> evicted = new ConcurrentHashMap<>();
Cache<Integer, String> cache = CacheBuilder.newBuilder()
.expireAfterAccess(2, SECONDS)
.removalListener((RemovalListener<Integer, String>) notification ->
evicted.put(notification.getKey(), notification.getValue()))
.build();
assert evicted.size() == 0 && cache.size() == 0;
cache.put(0, "a");
cache.put(1, "b");
cache.put(2, "c");
assert evicted.size() == 0 && cache.size() == 3;
sleepUninterruptibly(1, SECONDS);
assert evicted.size() == 0 && cache.size() == 3;
cache.put(3, "d");
assert evicted.size() == 0 && cache.size() == 4;
sleepUninterruptibly(1, SECONDS);
cache.cleanUp();
assert evicted.size() == 3 && cache.size() == 1;
Integer key = 2;
String value;
{
value = cache.getIfPresent(key);
if (value == null) value = evicted.get(key);
}
assert Objects.equals(value, "c");
您的实际代码需要有条件地将put 转换为evicted、清理evicted、管理多个evicted 对象(如果您同时运行长时间运行的事务或在具有不同线程的线程之间使用公共缓存)驱逐策略等,但希望这足以说明您的想法。
【讨论】: