LALA  
          日歷
          <2009年6月>
          31123456
          78910111213
          14151617181920
          21222324252627
          2829301234
          567891011

          導航

          留言簿(1)

          隨筆分類(31)

          文章分類(4)

          收藏夾(21)

          搜索

          •  

          積分與排名

          • 積分 - 29910
          • 排名 - 1389

          最新隨筆

          最新評論

          閱讀排行榜

           
          TB級別的網頁容器

          一個高性能的Web爬蟲,必須有一個合適的網頁容器。該容量最大的特點是要能夠通過URL直接存取網頁內容,并且要求有很高的性能,在一個千萬級別的容器中存取一萬次的時間應在1分鐘左右(普通PC上)。
          那么,有什么方式可以實現這個要求?
          首先,我們想到文件系統,將URL編碼(urlEncode,base64或hex都可以)后作為文件名直接存在文件系統的某個目錄下,從而實現通過URL直接存取的目的。但這種方式管理上會有很大的問題(試過在一次刪除十萬個文件的朋友就知道會有多慢),會產生大量的小文件,在某些操作系統上會極大地降低文件系統的性能。
          其次,放在數據庫中,將URL單獨存在一個字段里,為該字段建立索引,也可以實現按URL直接存取的目的,而且性能較好。但實際上這也不合適,幾百萬上千萬的網頁存放在數據庫中,占用近TB的空間,必然要求對數據庫有較大的投資,但從其他網站上采集的網頁使用次數很少,并且極為廉價可再次獲取,因此使用數據庫很不劃算。
          也可以自己實現一個容器,直接管理持久層的裸設備,在裸設備上建立一套通過URL尋址的機制,將會獲得最好的性能。但在容器量較小的情況這樣又顯很麻煩,因此采用拆衷的辦法,在文件系統的基礎上建立一組大文件和一組輔助文件,輔助文件實現通過URL定位該URL代表的網頁在大文件中的位置,從頁實現不隨文件數量增長而性能變化的快速存取。以下將描述一個簡潔的實現。

          我們知道,一個HashMap在容量為10和容量為100000時通過Key存取一個元素的性能基本相當,因此可以在HashMap的基礎上實現一個基于文件系統的FileMap。
          第一步,我們直接照抄HashMap中的散列算法:
          Java代碼
          public static int hash(Object x, int length) { 
              int h = x.hashCode(); 
              h += ~(h << 9); 
              h ^= (h >>> 14); 
              h += (h << 4); 
              h ^= (h >>> 10); 
              return h & (length - 1); 


          假設length等于10000,那么傳入一組URL通過hash()算法返回的值將基本平均分布在這1到10000,而不管這一組URL的具體內容到底是什么(URL之間要有差異,不能都相同或大部分相同,呵呵),這是整個實現最為關鍵的地方。在實際應用中length的值是隨著容量增長而不變化的,每次擴容后都需要將所有URL重新計算散列值,大家可以參考HashMap中的實現。

          第二步:存放文件內容
          實現存放內容的方法:假如現在需要存放一個URL和它的內容,那么必須在value.dat的最后寫入內容長度和內容本身(如果value.dat不存在,則需要先建立一個),并且返回一個內容長度起始字節在value.dat中的起始地址。

          第三步:存放鍵值
          實現存放鍵值的算法:得到內容的起始地址,計算[起始地址+URL]的長度,將該長度和[起始地址+URL]寫入鍵值輔助文件key.dat的最后(如果key.dat不存在,則需要先建立一個),并且返回該長度起始字節在key.dat的地址。

          第四步:存放散列值與鍵值地址的對應關系
          實現存放散列值與鍵值地址對應關系的算法:得到鍵值的起始地址后(地址長度為4字節,即為long類型的長度),通過hash()計算URL的散列值,假設散列值為3000的話,則將該地址寫入地址輔助文件address.idx的第12000-12004個字節。(以后再說散列沖突的情況)

          第五步:取URL內容的
              實現取URL內容的算法:假設URL已經存入FileMap,當需要通過URL取內容時,步驟如上:通過hash()計算URL的散列值,通過散列值從address.idx中取鍵值在key.dat中的地址,通過鍵值中內容在value.dat中的地址,即可取到URL對應的內容了。

          第六步:解決散列沖突
          hash()能將一組URL基本平均地分布在一塊地址上,但不可避免地會出現散列沖突的情況,即多個不同的URL獲得同一個散列值的情況,這時候第一個存入的URL將直接寫入address.idx中散列值對應的地址,其他的URL存入時需要將本身的鍵值地址寫入第一個URL在key.dat的記錄的末尾,以便存取時能夠通過第一個URL找到其他散列值相同的URL,從面解決散列沖突的問題。

          以上六步是實現一個TB級別的容器可以選擇的比較簡潔的過程,實際運用中還需要解決value.dat過大的問題(有時操作系統對文件大小有限制,必須形成value0.data,value1.data,value2.data等一組value文件,從而使得尋址進一步復雜),解決重新散列的問題,解決壓縮存取的問題。

          雖然存取一個URL使用了3個文件,但因address.idx和key.dat的體積都很小,使用時又都是直接定位,并且因頻繁被使用被磁盤的Cache以及操作系統的Cache緩存,時間性能消耗是非常小的。
          posted on 2009-06-04 21:04 Dest 閱讀(214) 評論(0)  編輯  收藏 所屬分類: Java
           
          Copyright © Dest Powered by: 博客園 模板提供:滬江博客
          主站蜘蛛池模板: 阳春市| 新密市| 莒南县| 江阴市| 望奎县| 临夏县| 黔江区| 枣强县| 开远市| 林西县| 长顺县| 普兰店市| 凭祥市| 博客| 漯河市| 青州市| 黄龙县| 黄骅市| 四子王旗| 桦南县| 安远县| 永川市| 茂名市| 靖安县| 剑河县| 卓尼县| 若尔盖县| 澄江县| 邓州市| 台南市| 桐乡市| 体育| 南华县| 镇江市| 纳雍县| 清苑县| 正阳县| 皮山县| 仙桃市| 化州市| 丹寨县|