![](/img/trans.png)
[英]Java ConcurrentHashMap is better than HashMap performance wise?
[英]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億個鍵值對應該不是問題。
絕對嘗試redis ,認為它能擊敗其他任何東西
您可以使用Berkeley DB ,它基本上是用C編寫的鍵/值存儲,以實現最終性能。 這是一個Oracle產品(雖然是開源的),所以我會認真對待。
如果您的應用程序必須在本地運行而不使用任何外部計算能力,那么沒有比直接內存訪問更高性能的解決方案:唯一可以為您提供更好性能的數據結構就是HashMap,這是數組。每個元素的訪問權限為O(1)。 但是,這需要預先知道您有多少項,每個元素具有唯一的尋址索引,並且還必須保留大量的相鄰內存。
在描述了適用於有限情況的數組之后,您有了HashTables,但是隨着數據大小的增長,沖突和動態調整大小的成本增加,並使性能變差。
您可以參考java.util.HashMap javadoc,也可以參考Wikipedia http://en.wikipedia.org/wiki/Hash_table以了解以下內容:
如果在構建HashMap時性能下降,而我實際上認為這是ConcurrentHashMap(如果並行構建它必須是線程安全的),則您可能想調查為什么會發生。
一個簡單但容易的開始就是將您的HashMap替換為TreeMap,TreeMap的性能是其大小的確定性函數,並比較這兩種性能。
另一方面,如果我誤解了您的問題,並且您有機會在多台計算機上擴展計算,那么正如有人已經指出的那樣,您在市場上有很多有趣的解決方案,我將在其中添加Cassandra。
這些解決方案通過在多個節點之間分配負載來提高性能,但是在每個節點內部使用眾所周知的算法進行快速有效的尋址。
對於問題和后續討論尚不清楚,但是您的查詢的本質是什么? 您之間的情況截然不同
a)在每個工作日內瀏覽所有約7億個網址,或
b)擊中約7億個URL中的一小部分。
那么:查詢數量與網址數量的比率是多少?
從您的描述中,聽起來您可能正在加載/卸載代表數組不同部分的不同文件……這建議使用隨機查詢,建議使用(b)。
同樣,我收集到您已經認識到“全內存”是不可行的(即,您已經破壞了跨多個文件的陣列),因此,最佳的磁盤訪問算法似乎是下一步的工作,不?
您是否已針對每個查詢嘗試了一個簡單的查找(n * arrayElementSize)來偏移文件,並僅將幾頁讀入內存(您是否知道每個鍵的最大數量的值?)。 您已經將(索引)基本索引放入了數組中,因此應該易於原型化。
我建議您使用Oracle Coherence Cache 。 您可以獲得HashTable
所有好處,它具有Map擁有的所有方法。
在性能方面,您可以根據需要存儲數據。請看一下。
在內存數據庫中使用開源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.