首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于时间戳的逐出缓存

基于时间戳的逐出缓存
EN

Stack Overflow用户
提问于 2014-12-18 21:41:50
回答 3查看 4.3K关注 0票数 4

我的服务端点每分钟都会收到一个度量列表以及它们的时间戳。如果度量传递某些条件,我们需要将它们存储在缓存中,以便以后可以访问它们。这项服务的存取功能如下:

代码语言:javascript
复制
List<Metrics> GetAllInterestingMetrics5Mins();
List<Metrics> GetAllInterestingMetrics10Mins();
List<Metrics> GetAllInterestingMetrics30Mins();

我的现代解决方案是使用3个番石榴缓存,基于时间的驱逐设置为5,10和15分钟。当有人调用上述函数之一时,我将从相关缓存中返回所有指标。

这有两个问题-

  1. 番石榴缓存根据值放在缓存中的时间(或根据设置的不同而访问)启动驱逐时间。现在可以延迟度量,因此时间戳将比将度量放入缓存的时间早。
  2. 我不喜欢创建3个缓存,当一个有30分钟的缓存就足够了,它增加了内存占用和缓存处理的复杂性。

有没有办法解决这两个问题在番石榴或任何其他开箱即用的缓存解决方案?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-12-19 10:24:00

关于专题1:

请不要混淆过期和驱逐。过期意味着缓存可能不再返回条目,并且可能发生在指定的时间点或持续时间之后。逐出是释放资源的动作,从缓存中删除条目。期满后,驱逐可能发生在同一时间或以后。

所有常见的缓存产品都不支持精确,也就是“时间点”,过期。在我们的应用程序中,我们经常需要这种用法,所以我花了一些精力在cache2k上来支持它。

以下是cache2k的蓝图:

代码语言:javascript
复制
static class MetricsEntry {

  long nextUpdate;
  List<Metrics> metrics;

}

static class MyEntryExpiryCalculator implements EntryExpiryCalculator<Integer, MetricsEntry> {
  @Override
  public long calculateExpiryTime(Integer _key, MetricsEntry _value, long _fetchTime, CacheEntry _oldEntry) {
    return _value.nextUpdate;
  }
}

Cache createTheCache() {
  Cache<Integer, MetricsEntry> cache =
    CacheBuilder.newCache(Integer.class, MetricsEntry.class)
      .sharpExpiry(true)
      .entryExpiryCalculator(new MyEntryExpiryCalculator())
      .source(new MySource())
      .build();
   return cache;
}

如果度量对象中有时间引用,则可以使用它,并且可以省略附加的条目类。sharpExpiry(true)指示cache2k的确切有效期。如果您忽略了这一点,过期时间可能会缩短几毫秒,但访问时间会稍微快一些。

关于专题2:

直接前进的方法是使用间隔分钟作为缓存键。

下面是一个缓存源(也称为缓存加载器),它严格返回前一间隔的指标:

代码语言:javascript
复制
static class MySource implements CacheSource<Integer, MetricsEntry> {
  @Override
  public MetricsEntry get(Integer interval)  {
    MetricsEntry e = new MetricsEntry();
    boolean crossedIntervalEnd;
    do {
      long now = System.currentTimeMillis();
      long intervalMillis = interval * 1000 * 60;
      long startOfInterval = now % (intervalMillis);
      e.metrics = calculateMetrics(startOfInterval, interval);
      e.nextUpdate = startOfInterval + intervalMillis;
      now = System.currentTimeMillis();
      crossedIntervalEnd = now >= e.nextUpdate;
    } while (crossedIntervalEnd);
    return e;
  }
}

这将返回10:00-10:05的度量,如果您在10:07上执行请求。

如果您只想立即计算过去间隔的度量,那么它就更简单了:

代码语言:javascript
复制
static class MySource implements CacheSource<Integer, MetricsEntry> {
  @Override
  public MetricsEntry get(Integer interval)  {
    MetricsEntry e = new MetricsEntry();
    long intervalMillis = interval * 1000 * 60;
    long startOfInterval = System.currentTimeMillis();
    e.metrics = calculateMetrics(startOfInterval, interval);
    e.nextUpdate = startOfInterval + intervalMillis;
    return e;
  }
}

put()相比,缓存源的使用具有优势。cache2k正在阻塞,因此,如果多个请求输入一个度量,则只启动一个度量计算。

如果你不需要毫秒的确切期限,你也可以使用其他的缓存。您需要做的事情是存储计算缓存值中的度量所需的时间,然后相应地更正过期时间。

那多保重!

票数 1
EN

Stack Overflow用户

发布于 2014-12-18 22:40:12

像番石榴和EHCache这样的缓存解决方案和你想要实现的东西之间有一个特殊的区别。这些缓存的唯一目的是以与getter函数工作相同的方式工作。因此,缓存的目的是通过其键检索单个元素,并将其存储以供进一步使用;在停止使用后将其逐出。

例如。

代码语言:javascript
复制
@Cacheable
public Object getter(String key){
...
}

这就是为什么从缓存中获取一整组对象的感觉有点像强迫缓存和驱逐策略与其最初的目的不同。

您需要的不是番石榴缓存(或其他缓存解决方案),而是可以通过计时器函数一次驱逐的集合。不幸的是,番石榴现在还不提供这种服务。您仍然需要应用程序提供的计时器函数,它将从缓存中删除所有现有元素。

因此,我的建议如下:

即使番石榴有可能以你想要的方式运行,你也会发现你并没有使用使番石榴真正有价值的特性,而且你正在“强迫”它的行为有所不同。因此,我建议您忘记番石榴实现,并考虑使用例如AbstractMap类的专门化,以及一个计时器函数,它每隔N秒钟就会删除它的内容。

这样,您就可以将所有条目放在一个缓存中,并且不再担心时间戳和条目添加到缓存的时间之间的差异。

票数 2
EN

Stack Overflow用户

发布于 2014-12-18 21:54:41

你考虑过使用像Deque这样的东西吗?只需将度量放在队列中,当您想检索最后N分钟的度量时,只需从最近的添加开始,并获取所有内容,直到您找到一个从>N分钟前开始的度量。您可以以类似的方式将太老的条目从另一端逐出。(从您的问题中,我不清楚Cache的关键/价值方面与您的问题有何关联。)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27556216

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档