2012-11-26 75 views
0

背景:我正在研究订购系统的分析系统。每天大约有100,000个订单,分析需要在最近N(例如100天)的月份内运行。相关数据适合内存。 N天后,所有订单都从内存缓存中逐出,过去一整天都被驱逐出境。订单可以创建或更新。基于日期缓存过期的缓存或MultiMap?

  1. 传统方法将使用ConcurrentHashMap<Date, Queue<Order>>。每天,表示过去N天以上的日期的键值将被删除。但是,当然,使用番石榴的重点在于避免这种情况。编辑:将Map更改为ConcurrentHashMap,查看问题的结尾以获得理由。

  2. 随着番石榴收藏,MultiMap <Date, Order>会更简单。驱逐类似,明确实施。

  3. 虽然Cache实现看起来很吸引人(毕竟,我正在实现一个缓存),但我不确定驱逐选项。驱逐只会每天发生一次,并且最好从缓存外发起,我不希望缓存必须检查订单的年龄。我甚至不确定缓存是否会使用MultiMap,我认为在这种情况下它是一个合适的数据结构。

因此,我的问题是:是否有可能使用与我所需要的规则使用并公开多重映射的语义,并允许外界本身从控制拆迁,特别是高速缓存(“删除所有订单较老比N天“)?

作为一个重要的说明,我对LoadingCache不感兴趣,但我确实需要批量加载(如果应用程序需要重新启动,必须​​从数据库中填充缓存,并在最后N天的订单)。

编辑:忘了提,必须同时,由于订单进来他们对以前的订单实时评估为同一客户或地点等

EDIT2地图:只要绊倒Guava issue 135。它看起来像MultiMap不是并发的。

+0

请参阅[番石榴问题#142](https://code.google.com/p/guava-libraries/issues/detail?id=142)('Cache'是'MapMaker'生成的'ConcurrentMap'的后继者)和[这个问题](http://stackoverflow.com/questions/737060/create-weak-multimap-with-google-collections)。 – Xaerxess

+0

关于编辑#2:您可以使用['Multimaps#synchronizedMultimap'](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Multimaps.html#synchronizedMultimap( com.google.common.collect.Multimap))拥有一个由指定的multimap_支持的同步(线程安全)multimap。 – Xaerxess

+0

@Xaerxess谢谢,我将不得不测试它是如何执行的;我担心它不会像ConcurrentHashMap那么好,在这种情况下,我将不得不回到使用JDK类(即问题中的方法#1)。 – wishihadabettername

回答

1

我在这里既不使用Cache也不使用Multimap。虽然我喜欢并使用它们,但在这里没有太多的收获。

  • 您想手动驱逐您的输入,所以Cache的功能在这里并不真正使用。
  • 您正在考虑ConcurrentHashMap<Date, Queue<Order>>,这在某种意义上比Multimap<Date, Order>更强大。

我会使用一个Cache,如果我想到了不同的逐出准则,如果我感觉就像失去它的任何条目随时是罚款。

您可能会发现您需要ConcurrentMap<Date, Dequeue<Order>>ConcurrentMap<Date, YouOwnQueueFastSearchList<Order>>或其他任何东西。这可能可以通过Multimap进行管理,但恕我直言,它变得更加复杂而不是简单。

我会问自己“我在这里使用CacheMultimap获得什么?”。对我来说,它看起来像普通的旧ConcurrentMap提供您所需要的一切。


绝不我建议这将与番石榴发生。相反,没有驱逐原因(容量,到期,...),它就像ConcurrentMap一样工作。这只是你所描述的感觉更像是Map而不是Cache

+0

我认为你是对的;早些时候我看到了这个评论“注意:如果你不需要Cache的特性,ConcurrentHashMap更具有内存效率 - 但是用任何旧的ConcurrentMap复制大多数Cache特性是非常困难或不可能的。”在http://code.google.com/p/guava-libraries/wiki/CachesExplained中,虽然Cache可以返回一个ConcurrentMap,但我认为这不值得使用它。 – wishihadabettername

1

恕我直言,最简单的做法是将订单的日期包括在订单记录中。 (我期望它已经是一个领域了)因为你只需要每天清理一次缓存,所以它不一定非常高效,只需要相当及时。

例如

public class Main { 
    static class Order { 
     final long time; 

     Order(long time) { 
      this.time = time; 
     } 

     public long getTime() { 
      return time; 
     } 
    } 

    final Map<String, Order> orders = new LinkedHashMap<String, Order>(); 

    public void expireOrdersOlderThan(long dateTime) { 
     for (Iterator<Order> iter = orders.values().iterator(); iter.hasNext();) 
      if (iter.next().getTime() < dateTime) 
       iter.remove(); 
    } 

    private void generateOrders() { 
     for (int i = 0; i < 120000; i++) { 
      orders.put("order-" + i, new Order(i)); 
     } 
    } 

    public static void main(String... args) { 
     for (int t = 0; t < 3; t++) { 
      Main m = new Main(); 
      m.generateOrders(); 
      long start = System.nanoTime(); 
      for (int i = 0; i < 20; i++) 
       m.expireOrdersOlderThan(i * 1000); 
      long time = System.nanoTime() - start; 
      System.out.printf("Took an average of %.3f ms to expire 1%% of entries%n", time/20/1e6); 
     } 
    } 
} 

打印

Took an average of 9.164 ms to expire 1% of entries 
Took an average of 8.345 ms to expire 1% of entries 
Took an average of 7.812 ms to expire 1% of entries 

10万台的订单,我希望它可以采取〜10毫秒这与其说是在深夜安静的时期承担。

BTW:如果您的OrderIds按时间排序,则可以使此效率更高。 ;)

0

您是否考虑过使用某种排序列表?它可以让你拉入口,直到你打出一个足够新鲜的留下来。当然这假定这是你的主要功能。如果你最需要的是使用hashmap进行O(1)访问,我的答案不适用。

+0

订单日期是方法#1中的关键,整个订单集合(存储在队列中)被驱逐。但问题更多的是关于#2和#3。 – wishihadabettername