分享

MapReduce设置CombineTextInputFormat处理小文件的小测试

本帖最后由 轩辕依梦Q 于 2015-5-31 19:06 编辑



当遇到小文件处理时,每个文件会被当成一个split,那么资源消耗非常大,hadoop支持将小文件合并后当成一个切片处理。(默认)

combinerIn.png
仍然以wordcount为例,依然设置默认块大小是128M,需要设置输入数据格式为CombinTextInputFormat,默认的输入格式是TextInputFormat.
comout2.png
输入为三个文件,输出为一个文件。

扩大文件大小和数量,再测试,依然是一个输出结果
comout3.png

那这里就想到一个问题,如果是在生产环境中,小文件的数量太多,那么累计起来的数量也是很庞大的,让一个maptask处理,显然效率低下,那是否可以设置多个切片呢?

简单看了一下CombineFileInputFormat的源码,里面重写了getSplits(JobContext job)方法,可以通过设置多个参数来改变split大小,能力有限,只看到有几个参数可以影响切片的大小,但具体怎么影响没看明白,然后就设置了其中一个参数,把切片的最大值分别设置为128M和300M,看看有什么结果,
wcout6.png
wcout4.png

分别产生了5个结果文件和2个结果文件,
至于这个切片具体怎么确定的,有知道的大神给讲一讲啊。坐等大神!!


已有(1)人评论

跳转到指定楼层
langke93 发表于 2015-6-1 00:30:04



在hadoop中划分是由InputFormat这个接口来定义的,其中有个getSplits方法。这里就有了一个新的不为人熟知的概念:Split。Split的作用是什么,Split和Block是什么关系,下面就可以说明清楚。
在Hadoop0.1中,split划分是在JobTracker端完成的((新API中应该是ResourceManager)),发生在JobInitThread对JobInProgress调用inittasks()的时候;而在0.18.3中是由JobClient完成的,JobClient划分好后,把split.file写入hdfs里,到时候jobtracker端只需要读这个文件,就知道Split是怎么划分的了。
划分只是一种逻辑上划分,目的是为了让Map Task更好的获取数据输入,仔细分析如下这个场景:

File 1 : Block11, Block 12, Block 13, Block 14, Block 15
File 2 : Block21, Block 22, Block 23

File1有5个Block,最后一个Block当然可能小于64MB;File2有3个Block

如果用户在程序中指定map tasks的个数,比如说是2(如果不指定的话maptasks个数默认是1),那么在
FileInputFormat(最常见的InputFormat实现)的getSplits方法中,首先会计算totalSize=8(可以对照源码看看,注意getSplits这个函数里的计量单位是Block个数,而不是Byte个数,后面有个变量叫bytesremaining仍然表示剩余的Block个数,有些变量名让人无语),然后会计算goalSize=totalSize/numSplits=4,对于File1,计算一个Split有多少个Block是这样计算的

long splitSize = computeSplitSize(goalSize, minSize, blockSize);

protected long computeSplitSize(long goalSize, long minSize, long blockSize) {
return Math.max(minSize, Math.min(goalSize, blockSize));
}

这里minSize是1(说明了一个Split至少包含一个Block,不会出现一个Split包含零点几个Block的情况),计算得出splitSize=4,所以接下来Split划分是这样分的:
Split 1: Block11, Block12, Block13,Block14
Split 2: Block15
Split 3: Block21, Block22, Block23
那用户指定的map个数是2,出现了三个split怎么办?在JobInProgress里其实maptasks的个数是根据Splits的长度来指定的,所以用户指定的map个数只是个参考。可以参看JobInProgress: initTasks()
里的代码:

  try {
   splits = JobClient.readSplitFile(splitFile);
  } finally {
   splitFile.close();
  }
  numMapTasks = splits.length;
  maps = new TaskInProgress[numMapTasks];


所以问题就很清晰了,还如果用户指定了20个map作业,那么最后会有8个Split(每个Split一个Block),所以最后实际上就有8个MapTasks,也就是说maptask的个数是由splits的长度决定的。

几个简单的结论:
1. 一个split不会包含零点几或者几点几个Block,一定是包含大于等于1个整数个Block
2. 一个split不会包含两个File的Block,不会跨越File边界
3. split和Block的关系是一对多的关系
4. maptasks的个数最终决定于splits的长度





回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条