簡體   English   中英

Java項目:提高HashMap(包括負載存儲)的性能

[英]Java Project: Make HashMap (including Load-Store) Performance Better

我正在嘗試為我們的服務器編寫代碼,其中我必須通過URL查找用戶訪問類型。

現在,一開始我們看到每天訪問1億個不同的URL。 現在,到現在為止,它已變成每天將近6億個不同的URL。

對於1億,我們所做的是:

1)使用並行數組構建一個HashMap,該並行數組的鍵是URL的一部分(以LONG表示),值是URL的另一部分(以INT表示)-鍵可以具有多個值。

2)然后搜索HashMap以查找訪問了多少次URL。

現在,隨着HashTable的變大,我們要做的是:

1)構建兩個/三個單獨的HashTable,然后將其加載和存儲(在常規文件系統上)以查找訪問URL的次數。

現在的問題是

1)雖然HashTable的性能相當不錯,但是在加載/存儲HashTable時代碼需要花費更多時間(我們正在使用文件通道,加載/存儲HashTable需要16-19秒-2億個條目-加載因子為0.5)

我們想問的是:

1)任何意見如何解決這個問題?

2)如何減少加載/存儲時間(我之前問過,但似乎文件通道是最好的方法)?

3)是否存儲一個大的HashTable(而不是內存)並重復緩存將是一個不錯的解決方案? 如果是這樣,該如何做(至少一些指針)。 我們嘗試使用

RandomAccessFile raf = new RandomAccessFile("array.dat", "rw");
IntBuffer map = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1 << 30).order(ByteOrder.nativeOrder()).asIntBuffer();

但是,其性能比以前差。

謝謝。

注意:

1)根據堆棧溢出的先前建議,我們使用了一些NoSQL DB,例如TokyoCabinet,但根據我們的經驗,自定義HashTable在1億個鍵值對上的性能要優於它。

2)無法預先讀取用於磁盤緩存的數據,因為當系統啟動時,我們的應用程序將開始運行,第二天系統啟動時,應用程序將開始運行。

我們忘記提及的是:

1)由於我們的應用程序是項目的一部分,並且將在一個小型園區中應用,因此我們假定訪問的URL不超過8億個。 因此,您可以認為600/700數據值是固定的。

2)我們主要關注的是性能。

3)我們必須在本地運行我們的應用程序。

編輯:我們的哈希圖代碼可以在這里找到。

最好將表作為內存映射的緩沖區來訪問。 這樣,您可以簡單地實現對文件的隨機訪問,而不必擔心加載和存儲,並將緩存留給操作系統。 我看到您當前的實現確實已經使用了內存映射的訪問方式進行讀寫,但仍將兩者之間的內容加載到Java堆中。 避免這種數據重復和復制! 將備份文件本身視為數據結構,僅在需要時才訪問您實際需要的部分。

在此文件中,如果您確實確定哈希沖突不是問題,則哈希映射將起作用。 否則,我會去那里的一棵B +樹 ,其中的節點大約等於您的硬盤頁面大小。 這樣,每個磁盤訪問將產生比僅單個鍵更多的可用數據,從而導致樹更淺,單個磁盤操作更少。

我猜想其他人會實現這樣的東西,但是如果您更喜歡自己的哈希映射實現,則可能更喜歡編寫自己的內存映射B +樹。

整個方法對我來說聽起來很可笑。 我收集到您真正想要實現的是每個不同URL的簡單訪問計數器。 就其本質而言,此數據經常被寫入,但很少讀取。

為此,我只需擁有一個數據庫表,並為每次訪問添加一個新條目(它也可以用作日志)。 當您需要找出訪問任何URL的頻率時,可以使用表中的SELECT COUNT來輕松完成此操作(取決於與URL條目一起存儲的額外數據量,您甚至可以進行約束計數,例如昨天的訪問頻率) ,上周等)。

這將所有工作拖到了真正需要結果的地步。

順便說一句,您也可以從Web服務器日志文件中檢索訪問計數,因此也許您不需要自己編寫任何數據。 先看看這個。

您可以使用JCS之類的緩存框架。 10億個鍵值對應該不是問題。

http://commons.apache.org/jcs/

絕對嘗試redis ,認為它能擊敗其他任何東西

您可以使用Berkeley DB ,它基本上是用C編寫的鍵/值存儲,以實現最終性能。 這是一個Oracle產品(雖然是開源的),所以我會認真對待。

如果您的應用程序必須在本地運行而不使用任何外部計算能力,那么沒有比直接內存訪問更高性能的解決方案:唯一可以為您提供更好性能的數據結構就是HashMap,這是數組。每個元素的訪問權限為O(1)。 但是,這需要預先知道您有多少項,每個元素具有唯一的尋址索引,並且還必須保留大量的相鄰內存。

在描述了適用於有限情況的數組之后,您有了HashTables,但是隨着數據大小的增長,沖突和動態調整大小的成本增加,並使性能變差。

您可以參考java.util.HashMap javadoc,也可以參考Wikipedia http://en.wikipedia.org/wiki/Hash_table以了解以下內容:

  • 計算多少錢?
  • 價值如何合理分配?
  • 您使用的負載系數是多少,即解決沖突將花費多少成本?
  • 您需要多長時間調整一次HashMap的大小,才能使其完全包含所有數據?

如果在構建HashMap時性能下降,而我實際上認為這是ConcurrentHashMap(如果並行構建它必須是線程安全的),則您可能想調查為什么會發生。

一個簡單但容易的開始就是將您的HashMap替換為TreeMap,TreeMap的性能是其大小的確定性函數,並比較這兩種性能。


另一方面,如果我誤解了您的問題,並且您有機會在多台計算機上擴展計算,那么正如有人已經指出的那樣,您在市場上有很多有趣的解決方案,我將在其中添加Cassandra。

這些解決方案通過在多個節點之間分配負載來提高性能,但是在每個節點內部使用眾所周知的算法進行快速有效的尋址。

對於問題和后續討論尚不清楚,但是您的查詢的本質是什么? 您之間的情況截然不同
a)在每個工作日內瀏覽所有約7億個網址,或
b)擊中約7億個URL中的一小部分。

那么:查詢數量與網址數量的比率是多少?

從您的描述中,聽起來您可能正在加載/卸載代表數組不同部分的不同文件……這建議使用隨機查詢,建議使用(b)。

同樣,我收集到您已經認識到“全內存”是不可行的(即,您已經破壞了跨多個文件的陣列),因此,最佳的磁盤訪問算法似乎是下一步的工作,不?

您是否已針對每個查詢嘗試了一個簡單的查找(n * arrayElementSize)來偏移文件,並僅將幾頁讀入內存(您是否知道每個鍵的最大數量的值?)。 您已經將(索引)基本索引放入了數組中,因此應該易於原型化。

我建議您使用Oracle Coherence Cache 您可以獲得HashTable所有好處,它具有Map擁有的所有方法。

在性能方面,您可以根據需要存儲數據。請看一下。

您可以嘗試HugeCollections ,我認為它是為此目的而編寫的

大量收藏
支持數百萬或數十億條目的館藏的圖書館。

特別是HugeMap

在內存數據庫中使用開源sqlite

如果我理解正確,那么您的數據結構就不會那么大

[(32 + 64) * 600 million] bits i.e. a 53.644 MB structure in memory

地圖數據結構也會占用一些空間。 我發現trove很難成為周圍內存效率最高的數據結構之一。 我將使用TLongIntHashMap來存儲長鍵和整數值。 它存儲原始圖元,以便您繞過Long和Integer內存對象

似乎您有一個只讀的數據集,該數據集不適合內存,並且您需要快速的鍵查找。 除非有一些可能的權衡,否則恐怕這里沒有解決方案。

如果您遍地訪問600M記錄,則無論您做什么,都將受到磁盤隨機訪問速度的限制(不加快順序訪問的速度)。 使用FileChannel.map直接訪問文件(不,不要讀取內存中文件的內容,只需在MappedByteBuffer上進行操作即可。操作系統將為您進行緩存)。 投資固態硬盤似乎是一種花錢的好方法(或者也許只是購買更多的內存?)。

這是校園環境,對嗎? 也許您可以在實驗室中使用計算機制作memcached / redis / etc。 簇? 也許您可以在下班時間使用它?

如果您同時訪問一些可識別的數據(即現在我們分析域a,然后是b等),則將數據拆分為存儲桶是個好主意。 就像保持相關數據在物理上接近一樣,以幫助緩存。 還是可以對URL進行預排序,然后以二進制搜索的方式訪問它們?

如果發生沖突的可能性是可以接受的,也許不存儲完整的URL,而是僅將64位的URL哈希作為哈希鍵是可以接受的? 在進行一些體操運動時,您可能根本不用存儲鑰匙就可以逃脫?

這是我目前的想法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM