2

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

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

我不希望这种情况发生。为了避免这种情况 - 一个想法是在概率上使条目比 TTL 早一点过期,这样服务就不会同时受到很多打击。

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

4

1 回答 1

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在您自己的类型中,这样您选择的任何行为都不会暴露给您的调用者。

于 2017-12-06T08:41:30.570 回答