使用番石榴缓存的概率早期到期

时间:2016-10-07 23:29:31

标签: java guava google-guava-cache

我有一个用expireAfterWrite(20, TimeUnit.MINUTES)创建的Guava缓存。创建时,缓存也会使用数十个条目进行初始化。当值到期时,客户端应该调用外部服务并刷新缓存。

问题是 - 所有初始化的值会在20分钟后同时到期,客户端几乎会在同一个实例上调用该服务几十次。

我不希望这种情况发生。为了避免这种情况 - 一个想法是probabilistically expire the entries比TTL早一点,这样服务就不会同时受到很多打击。

不幸的是,我没有看到使用Guava缓存执行此操作的选项 - 至少没有从Wiki或Javadoc弹出任何内容。还有其他图书馆吗?我试图避免为此目的编写自己的缓存实现。

1 个答案:

答案 0 :(得分:2)

缓存变暖是一个相当复杂的话题,所以有很多合理的方法,但不一定是单一的“正确”方法。一个简单的选择是在未来的交错时间简单地触发额外的写入,以便重置到期时间:

public static <K, V> void staggerCacheExpiration(
    Cache<K, V> cache, long maxExpiration, TimeUnit unit, ScheduledExecutorService scheduler) {
  for (Entry<K, V> e : cache.asMap().entrySet()) {
    long delay = ThreadLocalRandom.current().nextLong(0, maxExpiration);
    scheduler.schedule(() -> cache.put(e.getKey(), e.getValue()), delay, unit);
  }
}

依赖于Cache本身的任何方法的一个关键问题是,只有当调用者需要该值时,才会最终触发(可能很昂贵的)刷新。使用expireAfterWrite()refreshAfterWrite()可能更好 ,而是运行一个专用线程,负责按顺序更新每个密钥。通过让单个线程更新所有密钥,您自然会避免同时刷新多个密钥的任何热点,并且还可以避免阻止依赖缓存中的值的任何线程。

正如Ben Manes建议您可能更愿意将Cache封装在您自己的类型中,因此您选择的任何行为都不会暴露给您的来电者。