2017-08-09 72 views
0

我有一個項目列表和一個存儲產品信息和它的項目數據的地圖。 DB中大約有150k個項目和大約200k個產品(每個產品大約有1000到2000個項目映射到它)。 。在性能方面實現最佳功能

我需要纔是最重要的產品每一個項目出現在量的函數這是我所實現的功能:

public Map<Integer, Integer> getProductsNumberForItem(List<Item> itemsList, 
     Map<Integer, Map<Item, Integer>> itemsAmount) { 
    Map<Integer, Integer> result = new HashMap<>(); 
    for (Item i : itemsList) { 
     int count = 0; 
     for (Map<Item, Integer> entry : itemsAmount.values()) { 
      if (entry.containsKey(i)) { 
       count++; 
      } 
     } 
     result.put(i.getID(), count); 
    } 
    return result; 
} 

它可以在我的測試數據庫,裏面有少量的數據不錯,但當我在真實數據上運行它時,它需要很多時間(例如:它已經運行了一個小時,仍然沒有完成)。從邏輯上來看它清楚,我基本上執行了太多的操作,但不知道如何優化。

任何建議表示讚賞。

+4

您使用的是關係型數據庫嗎?如果你是那麼你應該能夠做大部分的工作。 – mrmcgreg

+0

將聚合推送到數據庫。 –

+0

@mrmcgreg是的,這只是我想到的,我可以檢索映射產品集的大小。 – Cap

回答

2

有兩種方式:

  • 最有效的:做計算在數據庫中執行的查詢。
    隨着count()聚合和group by子句,你應該得到一個更好的結果,因爲整個處理將由設計/優化來執行的DBMS執行。

  • 效率較低,但您可以試一試:像現在一樣檢索數據並使用多線程。
    隨着Java 8 parallelStream(),你可能會得到一個可以接受的結果,而沒有麻煩來自己處理同步。

+0

我已經實現了它,只需更正實體內的映射。不過謝謝。 – Cap

+0

沒問題。但是,如果關係是懶惰的,它將爲每個產品執行不同的查詢。這不是有效的。 – davidxxx

+0

哦,我沒有指定,如果它是急切或懶惰加載。是否有可能確定默認情況下的設置? – Cap

0

最好的選擇就是這種計算委託給分貝,避免了需要的所有數據傳輸到你的應用服務器。

如果這不是一個選項,那麼肯定可以改進你當前的算法。現在,對於列表中的每個項目,您正在循環所有產品;這是指數成本。

你能做到這一點(使用流,因爲ressoning更容易在我看來,以遵循,也允許添加一些改進,但同樣也沒有他們實現):

Stream<Item> productsItemsStream = itemsAmount.values().stream().flatMap(p -> p.keySet().stream()); 
Map<Item,Long> countByItemFound = productsItemsStream.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()); 
Map<Integer, Integer> result = itemsList.stream().collect(Collectors.toMap(Item::getID, i -> countByItemFound.getOrDefault(i.getID(), 0L).intValue())); 

通過這種方法,你會做一個完整的傳遞給產品項目。然後再傳遞給項目列表。這是線性成本。

Specificto流,你可以嘗試啓用並行性(向我的解決方案中添加parallelStream),但它並不完全授予性能提高;取決於幾個因素。我希望能夠看到提議的解決方案的性能,並且如果需要的話,可以在您的方案中使用和不使用parallelStream來分析性能。