2012-11-26 120 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