Hadoop上小檔案儲存處理
在Hadoop中小檔案是一個大問題 — 或者說, 至少, 他們在使用者的討論區域是比較熱門的話題. 在這篇文章中我將直面這個問題, 並提出一些常見的解決方案.
在HDFS中的小檔案問題
這裡討論的小檔案指的是那些在HDFS中小於HDFS塊大小(預設是64M)的檔案. 如果你儲存了很多這種小檔案, 或者你有很多這種小檔案 (如果你並沒有使用Hadoop), 這裡討論的問題是Hadoop不能處理大量的這種檔案.
每一個檔案, 目錄和塊在HDFS中代表一個物件儲存在namenode的記憶體中, 它們每一個佔用150 位元組, 根據經驗. 所以1000萬個檔案,每一個使用一個塊, 將大約需要3GB的記憶體. 遠遠超過當前的硬體處理水平. 當然10億個檔案直接沒法處理.
此外, HDFS是沒有能力去有效地訪問小檔案:它主要是專為序列流式訪問的大檔案設計的. 閱讀小檔案通常會導致從一個DataNode到DataNode檢索每個小檔案, 所有的這些訪問資料效率都是很低下的.
在MapReduce中小檔案問題
Map任務每次能處理一個塊的輸入 (使用的是預設的 FileInputFormat
). 如果檔案非常小而且還很多, 導致每次Map任務產生非常小的輸入, 同時這裡需要大量的map任務, 這樣增加了很多額外的開銷. 比較一個1GB 檔案分成16 64MB 塊, 10,000 或者 100KB 檔案. 10,000檔案每個檔案使用一個map任務, 這個任務比一個相等的大檔案要慢10或者上百倍.
這裡有一些特徵能夠減輕額外的開銷:多個任務使用一個JVM, 從而避免了一些JVM啟動開銷 (看mapred.job.reuse.jvm.num.tasks
屬性), 和MultiFileInputSplit
可以執行多個以上的分割任務.
小檔案怎麼產生的?
這裡至少有兩個場景
- 這些檔案是一個更大的邏輯檔案. 由於HDFS最近才支援追加,一個非常通用的儲存無界檔案(例如日誌檔案)的模式是寫在HDFS塊.
- 有的檔案一直比較小. 想象一個大的影象庫. 每一個檔案是一個不同的檔案, 沒有自然的方法把它們組合成一個更大的檔案.
這兩者需要不同的解決方案. 對於第一個例項, 檔案是有一個個記錄組成, 這個問題可以通過呼叫 HDFS’s sync()
對於第二種情況,通過一些方式來對檔案進行分組操作. Hadoop提供了幾種方案.
HAR files
Hadoop Archives (HAR 檔案)被引入到HDFS在0.18.0版本中為了減輕存放許多檔案在namenode記憶體中的問題. HAR 檔案的工作原理是在HDFS中建立一個分層的檔案系統. 一個 HAR檔案建立通過使用hadoop archive
命令,他通過執行一個MapReduce任務把HDFS中的小檔案進行打包. 對於客戶端使用HAR檔案系統沒有改變: 所有的原始檔案是可見的和可訪問的 (通過使用 har:// URL). 然而, 在HDFS上的檔案數量並沒有減少.
在HDFS上讀檔案比HAR上面效率要高一些, 事實上,可能會比較慢因為每個HAR檔案訪問需要兩個索引檔案的讀取以及資料檔案的讀取(見下圖). 雖然HAR檔案能夠作為MapReduce的輸入檔案 , HAR並沒有准許在進行Map操作的時候把所有的檔案當著一個塊來操作. 它應該是提供一個輸入的格式能夠在HARs提升改進的地方, 實際上它並沒有. 請注意 MultiFileInputSplit, 即使在 HADOOP-4565 通過選擇本地檔案分割來提高, 將也需要查詢每一個小的檔案. 通過與SequenceFile檔案效能比較是很有趣的, 所以說.現在HARs最常使用的是來處理文件.
Sequence Files
通常回答關於“小檔案問題” 是: 使用SequenceFile. SequenceFile檔案的設計思想是使用檔名作為key檔案內容作為值. 這在實踐中很好. 回到10,000 100KB 檔案問題, 你可以寫一個程式把他們放到一個單獨的SequenceFile檔案裡面, 並且你可以通過串流的形勢對其進行訪問(直接或者通過MapReduce) 在SequenceFile檔案上進行操作. 更神奇的地方是. SequenceFiles檔案能夠被可拆分, 所以MapReduce可以打破成塊,每一塊獨立操作. 它們也支援壓縮, 不像HARs. 塊壓縮是大多數情況下最好的選擇, 由於壓縮了幾個記錄塊(相當於每個記錄).
它可以將現有的資料轉化成為SequenceFiles. 然而,完全有可能在並行建立一系列sequencefiles. (Stuart Sierra 已經寫了一篇很有用的關於 post 轉換tar檔案到SequenceFile檔案 — 這種工具是很有益的, 看到更多的人會很好).最好的設計在寫入資料的時候直接寫入到SequenceFiles檔案裡面, 如果可能,而不是寫小檔案作為中間步驟.
不像HAR檔案, SequenceFile檔案沒法羅列出所有的keys,簡短的閱讀需要獲取通過整個檔案. (MapFiles檔案很像一個已經對key進行排序的 SequenceFiles 檔案, 維護部分索引, 所以他們不能羅列所有它們的keys — 如圖所示.)
SequenceFile檔案相當於Java的核心. TFile檔案被設計成跨平臺的, 是一個可以替代的 SequenceFile, 但它尚未提供.
HBase
如果你產生了很多小檔案, 然而, 根據訪問模式, 不同型別的儲存可能更合適. HBase 儲存資料通過MapFiles (索引SequenceFiles), 是一個很好的選擇如果你需要MapReduce流分析同時也需要隨機訪問檢視. 如果延遲是一個問題, 然而這裡有許多其他的選擇 — 看Richard Jones’ 傑出的survey of key-value stores.