zoukankan      html  css  js  c++  java
  • Hadoop是怎么分块Block的?

      不多说,直接上干货!

    hadoop的分块有两部分。
     

      第一部分就是数据的划分(即把File划分成Block),这个是物理上真真实实的进行了划分,数据文件上传到HDFS里的时候,需要划分成一块一块,每块的大小由hadoop-default.xml里配置选项进行划分。

    <property>  
      <name>dfs.block.size</name>  
      <value>67108864</value>  
      <description>The default block size for new files.</description>  
    </property>  

      这个就是默认的每个块64MB。数据划分的时候有冗余,个数是由以下配置指定的。

    <property>  
      <name>dfs.replication</name>  
      <value>3</value>  
      <description>Default block replication.   
      The actual number of replications can be specified when the file is created.  
      The default is used if replication is not specified in create time.  
      </description>  
    </property>    

      具体的物理划分步骤由Namenode决定。

     

      第二种划分是由InputFormat这个接口来定义的,其中有个getSplits方法。这里有一个新的概念:fileSplit。每个map处理一个fileSplit,所以有多少个fileSplit就有多少个map(map数并不是单纯的由用户设置决定的)。

      我们来看一下hadoop分配splits的源码:

    if ((length != 0) && isSplitable(fs, path)) {  
        long blockSize = file.getBlockSize();  
        long splitSize = computeSplitSize(goalSize, minSize, blockSize);  
        long bytesRemaining = length;  
          
        while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {  
            int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);  
            splits.add(new FileSplit(path, length-bytesRemaining, splitSize, blkLocations[blkIndex].getHosts()));  
            bytesRemaining -= splitSize;          
        }  
          
        if (bytesRemaining != 0) {  
          
        splits.add(new FileSplit(path, length-bytesRemaining, bytesRemaining,  blkLocations[blkLocations.length-1].getHosts()));        }  
      
    } else if(length!=0) {  
      
        splits.add(new FileSplit(path,0,length,blkLocations[0].getHosts()));  
      
    }else{  
      
        // Create empty hosts array for zero length files  
          
        splits.add(new FileSplit(path,0,length,new String[0]));  
    }  

      从代码可以看出,一个块为一个splits,即一个map,只要搞清楚一个块的大小,就能计算出运行时的map数。而一个split的大小是由goalSize, minSize, blockSize这三个值决定的。computeSplitSize的逻辑是,先从goalSize和blockSize两个值中选出最小的那个(比如一般不设置map数,这时blockSize为当前文件的块size,而goalSize是文件大小除以用户设置的map数得到的,如果没设置的话,默认是1),在默认的大多数情况下,blockSize比较小。然后再取bloceSize和minSize中最大的那个。而minSize如果不通过”mapred.min.split.size”设置的话(”mapred.min.split.size”默认为0),minSize为1,这样得出的一个splits的size就是blockSize,即一个块一个map,有多少块就有多少map。

      上面说的是splitable的情况,unsplitable可以根据实际情况来计算,一般为一个文件一个map。

      下面是摘自网上的一个总结:

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

      还有一点需要说明,在FileSplit类中,有一项是private String[] hosts;
      看上去是说明这个FileSplit是放在哪些机器上的,实际上hosts里只是存储了一个Block的冗余机器列表。
      比如有个fileSplit 有4个block: Block11, Block12, Block13,Block14,这个FileSplit中的hosts里最终存储的是Block11本身和其备份所在的机器列表,也就是说 Block12,Block13,Block14存在哪些机器上没有在FileSplit中记录。

      FileSplit中的这个属性有利于调度作业时候的数据本地性问题。如果一个tasktracker前来索取task,jobtracker就会找个 task给他,找到一个maptask,得先看这个task的输入的FileSplit里hosts是否包含tasktracker所在机器,也就是判断 和该tasktracker同时存在一个机器上的datanode是否拥有FileSplit中某个Block的备份。

      但总之,只能牵就一个Block,其他Block就要从网络上传。不过对于默认大多数情况下的一个block对应一个map,可以通过修改hosts使map的本地化数更多一些。 在讲block的hosts传给fileSplit时,hosts中的主机地址可以有多个,表示map可以从优先从这些hosts中选取(只是优先,但hdfs还很可能根据当时的网络负载选择不是hosts中的主机起map task)。

      知道这个特性之后,可以修改传回给fileSplit的hosts,在列表中只写block所在的那些hosts,这样hdfs就会优先将这些map放到这些hosts上去执行,由于hosts上有该block,就省掉了网络传输数据的时间。

      这样做的话,在job很多的时候,可能会出现hot spot,即数据用的越多,它所在hosts上的map task就会越多。所以在考虑修改传给fileSplit的时候要考虑平衡诸多因素。

  • 相关阅读:
    第四天——列表(一)
    第十二天—collections模块(十二)
    第十二天——sys模块(十)
    第十二天——os模块(九)
    第十二天——序列化模块(八)
    第十二天——random模块(七)
    第十二天——from ... import ...的使用(三)
    第十二天——import 使用(二)
    vue系列---------vuejs基础学习1.0
    前端随心记---------谈谈开发的工作规范
  • 原文地址:https://www.cnblogs.com/zlslch/p/7647089.html
Copyright © 2011-2022 走看看