切片原理

1.InputFormat

当数据进入到Map时,需要用到Inputformat抽象类,抽象类中有两个抽象方法

public abstract class InputFormat<K, V> {
    
    // 获取切片信息
  public abstract 
    List<InputSplit> getSplits(JobContext context
                               ) throws IOException, InterruptedException;
	// 创建RecordReader对象,该对象是真正用来读取数据的对象。
  public abstract 
    RecordReader<K,V> createRecordReader(InputSplit split,
                                         TaskAttemptContext context
                                        ) throws IOException, 
                                                 InterruptedException;

}


2.FileInputFormat

/**
* 作为子类FileInputFormat也是一个抽象类,该类实现了父类InputFormat中getSplits方法
*
*/

public List<InputSplit> getSplits(JobContext job) throws IOException {
    StopWatch sw = new StopWatch().start();
    
    /*
    通过job对象拿到配置文件获得值: 
    SPLIT_MINSIZE("mapreduce.input.fileinputformat.split.minsize")
    如果没有配置就返回1,配置了就返回配置的值(在Driver中配置的值)	 	
   	public static long getMinSplitSize(JobContext job) {
    	return job.getConfiguration().getLong(SPLIT_MINSIZE, 1L);
    }
    
    getFormatMinSplitSize():值返回为1
    
    获取切片的最小值,若不配置返回minSize就为1,若有配置返回minSize就为调整的大小
    */
    long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
    
    /*
    通过配置文件获取SPLIT_MAXSIZE的值("mapreduce.input.fileinputformat.split.maxsize")
   	如果没有配置就返回默认值Long.MAX_VALUE:0x7fffffffffffffff
	public static long getMaxSplitSize(JobContext context) {
    	return context.getConfiguration().getLong(SPLIT_MAXSIZE, 
                                              Long.MAX_VALUE);
    
    获取切片最大值,若是不配置就返回默认值。
    */
    long maxSize = getMaxSplitSize(job);

    // generate splits
    List<InputSplit> splits = new ArrayList<InputSplit>();
    List<FileStatus> files = listStatus(job);

    boolean ignoreDirs = !getInputDirRecursive(job)
      && job.getConfiguration().getBoolean(INPUT_DIR_NONRECURSIVE_IGNORE_SUBDIRS, false);
    // 遍历files中的内容
    for (FileStatus file: files) {
        //如果是目录或者忽略的就不切片了
      if (ignoreDirs && file.isDirectory()) {
        continue;
      }
        //获取路径和文件长度
      Path path = file.getPath();
      long length = file.getLen();
        
        //校验
      if (length != 0) {
        BlockLocation[] blkLocations;
        if (file instanceof LocatedFileStatus) {
          blkLocations = ((LocatedFileStatus) file).getBlockLocations();
        } else {
          FileSystem fs = path.getFileSystem(job.getConfiguration());
          blkLocations = fs.getFileBlockLocations(file, 0, length);
        }
          //判断文件是否可切
        if (isSplitable(job, path)) {
            //获取块大小
          long blockSize = file.getBlockSize();
            
            /*
            获取切片大小:
            blockSize:128M
            minSize:1
            maxSize:0x7fffffffffffffff
			protected long computeSplitSize(long blockSize, long minSize,
                                  long maxSize) {
                
                //返回切片大小128M(如果想要调整切片大小:调整maxSize(可使切片大小>块大小))
                //调整minSize(可是切片大小<块大小)但是要考虑跨界点读取,具体情况具体分析了
    			return Math.max(minSize, Math.min(maxSize, blockSize));
  			}
            */
          long splitSize = computeSplitSize(blockSize, minSize, maxSize);
			
            //文件剩余大小
          long bytesRemaining = length;
            
            /*	
            	判断是否可以切片:文件剩余大小/片大小 > 1.1就为true,表示可以切片
            	来回循环直到文件剩余大小/片大小 < 1.1
            	这里SPLIT_SLOP不选择1.0,考虑到为剩余大小开启一个MapTask会消耗资源
            */
          while (((double) bytesRemaining)/splitSize小于1.0大于0的时候在这里切片  > SPLIT_SLOP) {
            int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
              
              /*
                如果可以切片就调用makeSplit()获取
            	protected FileSplit makeSplit(Path file, long start, long length, 
                                String[] hosts, String[] inMemoryHosts)
                Path file:获取路径
                length-bytesRemaining:切片开始的位置
                splitSize:偏移量(长度)
                blkLocations[blkIndex].getHosts():更据数组的索引获取地址
                blkLocations[blkIndex].getCachedHosts():获取副本的值
                
                将生成信息的对象放入splits数组中
              */
            splits.add(makeSplit(path, length-bytesRemaining, splitSize,
                        blkLocations[blkIndex].getHosts(),
                        blkLocations[blkIndex].getCachedHosts()));
              //剩余的大小
            bytesRemaining -= splitSize;
          }

            /*
            	当剩余bytesRemaining/splitSize小于1.0大于0的时候在这里切片 
            */
          if (bytesRemaining != 0) {
              //获取块的索引位置
            int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
              
              /*
              	当剩余的 bytesRemaining/片大小于1.1且不为零时,整体且成一片
              	将生成信息的对象放入splits数组中
              */
            splits.add(makeSplit(path, length-bytesRemaining, bytesRemaining,
                       blkLocations[blkIndex].getHosts(),
                       blkLocations[blkIndex].getCachedHosts()));
          }
        } 
          ...
              /*
              剩下的就让他跑吧.... 最后在JobSubmitter中的int maps = writeSplits(job, 					submitJobDir);执行了
              */
  }
在Go语言中,切片(slice)是一个动态数组,它提供了对数组的部分或全部元素的访问和操作功能。切片的底层是通过数组实现的,但与数组相比,切片具有更强大的功能和灵活性。切片的底层实现原理是使用了一个结构体(runtime.slice)来表示切片的结构信息,包括指向底层数组的指针、切片的长度和容量等信息。切片的长度表示切片中实际存储的元素个数,而容量则表示切片底层数组的长度。切片可以根据需要动态扩容,当切片的长度超过了容量时,会重新分配更大的底层数组,并将原来的元素复制到新的底层数组中。这个过程是由Go语言的运行时系统自动完成的,不需要程序员手动操作。使用切片作为参数传递时,性能损耗很小,因为切片作为参数是传递的是一个结构体实例。切片作为参数具有比数组更强大的功能和灵活性,因此在Go语言中推荐使用切片而不是数组作为参数。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Golang切片的底层实现原理](https://blog.csdn.net/m0_72560813/article/details/129231704)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值