螞蟻絆倒大象,不起眼的小文件竟拖了Hadoop大數(shù)據(jù)的后
HDFS作為Hadoop生態(tài)系統(tǒng)的分布式文件系統(tǒng),它被設(shè)計(jì)用來(lái)存儲(chǔ)海量數(shù)據(jù),特別是TB、PB量級(jí)別的數(shù)據(jù)。它的設(shè)計(jì)的初衷也是存儲(chǔ)大文件,而如果HDFS上存在大量的小文件,會(huì)對(duì)系統(tǒng)性能帶來(lái)嚴(yán)重的問(wèn)題。本文想跟大家聊下小文件的處理。
本文的小文件是指那些大小比HDFS的block (Hadoop 2.x的默認(rèn)大小128MB)小的多的文件。在HDFS中,文件元信息,例如位置、大小、分塊信息等這些存儲(chǔ)在Namenode的內(nèi)存中,每一個(gè)object占用150 bytes的內(nèi)存。文件個(gè)數(shù)越多相應(yīng)的也會(huì)占用Namenode更多的內(nèi)存。何況HDFS主要是為了流式的訪(fǎng)問(wèn)大文件而設(shè)計(jì)的,讀取眾多的小文件顯然也是非常低效的。
▌Hadoop小文件的3類(lèi)常見(jiàn)情況的處理
1、HDFS中存儲(chǔ)包含了大量小文件
現(xiàn)象:在HDFS上已經(jīng)存在了大量的小文件和目錄。
方案:通過(guò)調(diào)用HDFS的sync()方法和append()方法,將小文件和目錄每隔一定時(shí)間生成一個(gè)大文件,或者可以通過(guò)寫(xiě)程序來(lái)合并這些小文件。
2、MapReduce的輸入包含大量小文件
現(xiàn)象:輸入文件中存在大量的小文件
MapReduce程序的Map任務(wù)(task)一次可以處理一個(gè)塊(block)大小的輸入數(shù)據(jù)(默認(rèn)使用FileInputFormat)。如果一個(gè)輸入文件的大小大于block,那么會(huì)拆成兩個(gè)或多個(gè)task進(jìn)行處理;如果小于block,也會(huì)用一個(gè)task處理該文件。需要處理的數(shù)據(jù)如果分散存儲(chǔ)在許多小文件中,就會(huì)產(chǎn)生大量的map task,如果小文件個(gè)數(shù)非常多,這會(huì)使處理時(shí)間變的很慢。
方案:有3種
1)Hadoop Archive:
Hadoop Archive是一個(gè)高效地將小文件放入HDFS塊中的文件存檔工具,它能夠?qū)⒃S多小文件打包成一個(gè)HAR文件,這樣會(huì)同時(shí)減少Namenode的內(nèi)存使用。
2)Sequence File:
Sequence File由一系列的二進(jìn)制key/value組成,如果key為小文件名,value為文件內(nèi)容,則可以將大批小文件合并成一個(gè)大文件。
1)和2)這里不做介紹,可以參考 ??
http://blog.cloudera.com/blog/2009/02/the-small-files-problem
3)CombineFileInputFormat:
Hadoop有一個(gè)專(zhuān)門(mén)的類(lèi)CombineFileInputFormat?來(lái)處理小文件,它根據(jù)一定的規(guī)則,將HDFS上多個(gè)小文件合并到一個(gè)InputSplit中,同時(shí)啟動(dòng)適量的Map來(lái)處理這里面的文件,以減少M(fèi)R整體作業(yè)的運(yùn)行時(shí)間。CombineFileInputFormat類(lèi)繼承FileInputFormat,主要重寫(xiě)了ListgetSplits(JobContext var1)方法,我們可以設(shè)置mapreduce.input.fileinputformat.split.minsize.per.node、mapreduce.input.fileinputformat.split.minsize.per.rack和mapreduce.input.fileinputformat.split.maxsize?參數(shù)的設(shè)置來(lái)合并小文件。其中mapreduce.input.fileinputformat.split.maxsize參數(shù)至關(guān)重要,如果沒(méi)有設(shè)置這個(gè)參數(shù)(默認(rèn)沒(méi)設(shè)置),那么同一個(gè)機(jī)架上的所有小文件將組成一個(gè)InputSplit,最終由一個(gè)Map Task來(lái)處理。如果設(shè)置了這個(gè)參數(shù),那么同一個(gè)節(jié)點(diǎn)(node)上的文件將會(huì)組成一個(gè)InputSplit。
InputSplit包含的HDFS塊信息存儲(chǔ)在CombineFileSplit?類(lèi)中。該類(lèi)包含了每個(gè)塊文件的路徑、起始偏移量、相對(duì)于原始偏移量的大小和這個(gè)文件的存儲(chǔ)節(jié)點(diǎn)。CombineTextInputFormat告訴MR程序如何讀取組合的InputSplit,具體如何解析CombineFileSplit中的文件主要在CombineFileRecordReader中實(shí)現(xiàn)。該類(lèi)封裝了TextInputFormat的RecordReader,并對(duì)CombineFileSplit中的多個(gè)文件循環(huán)遍歷并讀取其中的內(nèi)容。
樣例代碼如下:
日志輸出:
可以從日志中很清楚的看出input文件數(shù)為T(mén)otal input paths to process : 152,通過(guò)CombineFileInputFormat處理后splits為mapreduce.JobSubmitter: number of splits:1,map數(shù)為L(zhǎng)aunched map tasks=1??梢孕薷膍apreduce.input.fileinputformat.split.maxsize參數(shù),觀(guān)察Map Task的個(gè)數(shù)變化。
3、Hive小文件問(wèn)題
現(xiàn)象1:?hive輸入的文件過(guò)多
方案:設(shè)置mapper?輸入文件合并參數(shù)
現(xiàn)象2:hive執(zhí)行中間過(guò)程生成的文件過(guò)多
方案:設(shè)置中間過(guò)程合并參數(shù),盡量避免小文件
現(xiàn)象3:hive輸出結(jié)果生成的文件過(guò)多
方案:一種是調(diào)整reducer個(gè)數(shù),另一種是調(diào)整reducer大小
/ 參考文章 /
1、https://hadoop.apache.org/docs/r2.7.1/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html
2、https://cwiki.apache.org/confluence/display/Hive/Configuration+Properties
3、http://blog.cloudera.com/blog/2009/02/the-small-files-problem/