源码分析Hadoop FileInputFormat如何分片

2023-11-12

        Hadoop采用的是分布式并行计算的模式来处理大数据,在处理时必然要对数据进行分片,将数据由大化小,将一个大的任务化为几个小的任务,这就是hadoop处理大数据的核心思想。

        这里要讨论的是hadoop对数据进行分片的方案,这里的分片是逻辑上的,不同于Hdfs对数据的分块,分片并没有改变数据的存储位置。分片在hadoop中由InputFormat这个类体系来完成。

        先来看看InputFormat类的完整体系结构。 

           很容易看出来这个类体系的祖宗是一个抽象类:InputFormat,这个抽象类定义了两个抽象方法getSplitscreateRecordReader,如下:
public abstract     List<InputSplit> getSplits(JobContext context
                               ) throws IOException, InterruptedException;

  /**
   * Create a record reader for a given split. The framework will call
   * {@link RecordReader#initialize(InputSplit, TaskAttemptContext)} before
   * the split is used.
   * @param split the split to be read
   * @param context the information about the task
   * @return a new record reader
   * @throws IOException
   * @throws InterruptedException
   */
  public abstract    RecordReader<K,V> createRecordReader(InputSplit split,
                                         TaskAttemptContext context
                                        ) throws IOException, 
                                                 InterruptedException;</span>


         第一个函数getSplits就起将数据分片的作用,第二个函数读取分片的数据,组织成键值对的格式,Map函数处理的键值对就是由这个函数返回的RecordReader对象读取的。

        下面我们从源代码中看看Hadoop是如何对数据进行分片的,从上面的类体系继承图可以看到,FileInputFormat类直接继承了InputFormat类,该类实现了getSplits函数,但没有实现createRecordReader函数,所以它还是一个抽象类。那么也就是说,FileInputFormat类实现了对数据分片的功能。

        打蛇打七寸,直奔FileInputFormat源码中的getSplits函数:

/** 
   * Generate the list of files and make them into FileSplits.
   */ 
  public List<InputSplit> getSplits(JobContext job
                                    ) throws IOException {
   <span style="color:#ff0000;">long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));</span>
   <span style="color:#cc0000;">long maxSize = getMaxSplitSize(job);</span>

   // generate splits
    List<InputSplit> splits = new ArrayList<InputSplit>();
    List<FileStatus>files = listStatus(job);
    for (FileStatus file: files) {
      Path path = file.getPath();
      FileSystem fs = path.getFileSystem(job.getConfiguration());
      long length = file.getLen();
     BlockLocation[] blkLocations = fs.getFileBlockLocations(file, 0, length);
<span style="background-color: rgb(255, 255, 102);">if ((length != 0) && isSplitable(job, path)) { 
      <span style="color:#cc0000;"> long blockSize = file.getBlockSize();</span>
       <span style="color:#ff0000;">long splitSize = computeSplitSize(blockSize, minSize, maxSize);</span>

       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]));
      }
  }</span>
    
    // Save the number of input files in the job-conf
    job.getConfiguration().setLong(NUM_INPUT_FILES, files.size());

    LOG.debug("Total # of splits: " + splits.size());
    return splits;
  }</span>

        好戏开场了,前戏。

        第一步,确定分片大小

        诸君注意上面的四行红色代码:

        第一行红色代码找出分片的最小长度,它调用了两个函数getFormatMinSplitSize(), getMinSplitSize(job)。getFormatMinSplitSize返回值是1,getMinSplitSize从配置文件中获取我们配置的分片最小长度,获取的是配置文件中mapred.min.split.size的属性值。二者取其大作为分片的最小长度。

        第二行红色代码从配置文件中读取我们配置的分片最大长度,获取的是配置文件中的“mapred.max.split.size”属性值,如果该值没有配置,返回Long.Max_value。

        第三行红色代码获取文件该文件文件块的大小。

        第四行红色代码,调用了一个函数computeSplitSize。有必要看看它的源码:

<span style="font-size:18px;">  protected long computeSplitSize(long blockSize, long minSize,
                                  long maxSize) {
    return Math.max(minSize, Math.min(maxSize, blockSize));
  }
</span>

       会发现FileInputFormat分片的大小不能超过文件单个分块的长度,分片可以小于等于数据块的长度,但不能大于。默认情况下如果我们不配置配置文件中的“mapred.max.split.size”属性值的话,分片大小为块的大小,默认块大小64M。 

        第二步,高潮来了,开始分片了。

     为了方便查看,我们把上面黄色区域的代码搬到下面来看。

<span style="color:#33ff33;">if ((length != 0) && isSplitable(job, path))</span> {//如果文件长度不为0并且文件允许分割
        long blockSize = file.getBlockSize();
        long splitSize = computeSplitSize(blockSize, minSize, maxSize);

        long bytesRemaining = length;
       <span style="background-color: rgb(51, 51, 255);"><span style="color:#ffffff;"> 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;
        }</span></span>
        
        if (bytesRemaining != 0) {
          splits.add(new FileSplit(path, length-bytesRemaining, bytesRemaining, 
                     blkLocations[blkLocations.length-1].getHosts()));
        }
}<span style="color:#66ff99;"> else if (length != 0) </span>{//长度不等于0但不许对文件分割,即不能对文件分片,整个文件作为一个分片。
        splits.add(new FileSplit(path, 0, length, blkLocations[0].getHosts()));
} <span style="color:#33ff33;">else</span> {//文件长度为0 
    //Create empty hosts array for zero length files
        splits.add(new FileSplit(path, 0, length, new String[0]));
      }

        先看上面三行绿色的代码,从总体上了解一下分片的策略:

        第一行绿色代码表示如果文件中有数据并且允许分割,则按照之前的分片长度和下面要详解的分片算法进行分片。

        第二行绿色代码表示如果文件中有数据但不允许对文件进行分割就将整个文件作为一个分片。

        第三行绿色代码表示如果文件长度为0,表示文件没有数据,返回一个空的分片。

        好,高潮开始吧。做好准备啦,搞懂会爽死的。

        蓝底白字的部分是核心。

         首先问自己一个问题,while的循环的条件表示什么意思呢?这个式子本身很好懂,bytesRemaining表示每次分片之前文件的长度,逐次递减, bytesRemaining/splitSize为了判读文件剩余的部分还够不够按照splitSize表示的长度分片,当bytesRemaining/splitSize不大于1.1时不再循环分片。

         善于思考的人,要再问一个问题,为什么要是大于1.1呢,这是为了保证某个分片不致过短。比如,要分片的文件的长度只比分片长度大一点点,就应该将整个文件作为一个分片,而不是将其分割为两个分片,因为这样会导致第二个分片的长度太短。

        解释的太细了,会降低读者的思考能力吧。

        满足分片条件的话,开始对文件进行分片。文件在物理上是被分块存储在各个节点上,分片对象FileSplit应该包含该分片的起始位置所在的块。那么源码是找到分片是从哪个块开始的呢?哈哈,请看下回分解。

        首先,看贴出来的第一大段源码中白纸黑字的那一行,这行将指定文件对应的所有块都查找出来。

        接下来,就要按照splitSize代表的长度循环从文件的开头所在的块截取分片,并确定每个分片的起始位置所在的块。看什么蓝底红字的那一行,调用了一个函数getBlockIndex(blkLocations, length-bytesRemaining);该函数第一个参数是要分片的这个文件所有的分块组成的集合,第二个参数表示每次分片之后,文件的偏移量,这个偏移量很简单啦,比如一个128M的文件,读取64M之后,那此时这个偏移量就是64,表示文件未被分片的起始位置。把这个函数的源码贴出:

      

  protected int getBlockIndex(BlockLocation[] blkLocations, 
                              long offset) {
    for (int i = 0 ; i < blkLocations.length; i++) {
     if ((blkLocations[i].getOffset() <= offset) &&(offset < blkLocations[i].getOffset() + blkLocations[i].getLength())){
         return i;
      }
    }
    BlockLocation last = blkLocations[blkLocations.length -1];
    long fileLength = last.getOffset() + last.getLength() -1;
    throw new IllegalArgumentException("Offset " + offset + 
                                       " is outside of file (0.." +
                                       fileLength + ")");
  }
  

        for循环表示对文件对应的块进行遍历,满足if条件的下标i对应的那个块就是该分片的起始位置所在的块,将这个索引值返回。看完这句话,如果这个函数的解释就到此结束,那你肯定云里雾里。我靠,这是神马逻辑,绝对骂娘。现在国人写的很多技术书有很多这种内容,个人比较喜欢外文的参考书。这里的关键是要搞懂if条件代表的是什么。这个算法设计的真他妈巧妙,所以要成为编程高手,源码绝对要看。

        下面要举例详细讲解。

         必须先搞定几个表达式的内涵先:blkLocations[i].getOffset()表示每个块在这个文件的偏移量,偏移量不用解释了吧,就是块的起始数据在整个文件中的位置。如果你还不懂,恭喜你,你有可能先飞,因为你笨。blkLocations[i].getLength()表示块的长度。offSize=length-bytesRemaining,bytesRemaining -= splitSize(bytesRemaining 的初始值是文件的长度的length

        假设一个文件有128M,对于hadoop这个巨人来说,这个是不是太小了,举个例子而已,不必当真。

        假设分块的时候安装40M的块大小进行分块,会分为四个块,第四块只有8M数据。

        假设分片长度是30M。操,一堆假设。

        我们来列个表:共有四个块,所以有四行。

                                             blkLocations[i].getOffset()                            blkLocations[i].getOffset() + blkLocations[i].getLength()

                      block[0]                        0                                                                           40                                                                                                                                         

                      block[1]                       40                                                                         80                                                                        

                      block[2]                       80                                                                         120

                      block[3]                      120                                                                       160

        当第一次调用这个函数时offSize=0,0<=0<40,第一个块符合条件,所以第一个分片的开始位置在第一个分块。

        第二次调用时offsize=30,会发现还是符合第一个分块的添加,0<=30<40,所以第二个分片的起始位置也在第一个分块。

        第三次调用时offsize=60,满足第二个块的条件,40<60<80,第三个分片的起始位置位于第二个分块。

        第四次调用时offsize=90,满足第三个块的添加 ,80<90<120,第四个分片的起始位置在第三个分块。

        第五次调用时offsize=120 ,满足第四个块的添加,120<=120<128,第五个分片的起始位置位于第四个块。

        然后再FileSplit中记下每个分片的起始位置所在的块,在根据偏移量从块中读取数据。

        讲完了,懂了没,反正我是懂了。

                      
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

源码分析Hadoop FileInputFormat如何分片 的相关文章

  • 韩式多用动态图(甜蜜女孩)

  • 离线在Jenkins安装CoBOT安装插件

    最近在某金融客户做POC 把CoBOT安装在Jenkins上面 当前Jenkins版本没有任何插件 安装后由于是云桌面没有连接互联网或已经设置访问策略 无法进行在线安装插件 所以只能下载插件后再安装 在网络上搜索Jenkins插件 下载到两
  • 【SpringCloud】application.yml和 bootstrap.yml 区别

    1 首先yml和properties文件都是属于配置文件 功能一样 主要是区别于application和bootstrap的加载顺序 Bootstrap yml bootstrap properties 在application yml a
  • caffe问题Check failed: registry.count(type) == 1 (0 vs. 1) Unknown layer type: Python

    caffe中输入层使用python时 出现问题 Check failed registry count type 1 0 vs 1 Unknown layer type Python 解决方法 在caffe目录下Makefile confi
  • va_start和va_end使用详解

    本文主要介绍va start和va end的使用及原理 介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理 1 在C中 当我们无法列出传递函数的所有实参的类型和数目时 可以用省略号指定参数表 void foo void foo par
  • 手机抓包fiddler配置及使用教程

    本文基于Fiddler4讲解基本使用 fiddler抓包原理 注意 Fiddler 是以代理web服务器的形式工作的 它使用代理地址 127 0 0 1 端口 8888 当Fiddler退出的时候它会自动注销 这样就不会影响别的 程序 不过
  • libsvm库简介及使用

    libsvm是基于支持向量机 support vector machine SVM 实现的开源库 由台湾大学林智仁 Chih Jen Lin 教授等开发 它主要用于分类 支持二分类和多分类 和回归 它的License是BSD 3 Claus
  • Terdata 基础 第三课(参数宏)

    1 宏不是ANSI标准支持的 但大部分RDBMS都支持宏 在Teradata中 在ANSI和BTET缺省模式下都可以创建和执行宏 只不过在ANSI模式下会给出警告信息 1 1 参数宏 宏中可以包含可替代值的变量 CREATE MACRO d
  • linux shell 按行循环读入文件方法

    linux shell 按行循环读入文件常用代码如下 bin bash printf n echo cat file whiel read line cat test txt while read line do echo line don
  • wofstream,wcout无法输出unicode的真相

    之前我转载过一篇ofstream和wofstream与中文输出问题 让我初步知道如何解决这类问题 第一次我没有在意 按照文章中做的方法去做 然后程序就运行正常了 我试图去记住这些规则 但是我后来发现 太难了 以至于我在最近一次使用到 std
  • 程序编程代码大全_CNC加工中心程序代码大全,数控加工必备!

    数控机床的可编程功能分为两类 一类用来实现刀具轨迹控制即各进给轴的运动 如直线 圆弧插补 进给控制 坐标系原点偏置及变换 尺寸单位设定 刀具偏置及补偿等 这一类功能被称为准备功能 以字母G以及两位数字组成 也被称为G代码 另一类功能被称为辅
  • 免费赠票

    Cloud Ace 受邀参加 GTC2022 全球流量大会 助力中国企业扬帆出海 大会将在 2023 年 2 月 28 日 3 月 1 日举行 地点就在福田会展中心 6 号展馆 大会门票实行收费制 您可以扫码填写 Cloud Ace 的报名
  • GLSL语言基础

    定义 GLSL释义叫做OpenGL着色器编程语言 是为图形计算量身定制的 它包含一些针对向量和矩阵操作的有用特性 变量名字 变量名称的命名规范与C语言相同 可以使用字母 数字 以及下划线来组成变量的名字 但数字不能作为变量名称的第一个字符
  • OBS直播软件-简介

    转自 https jingyan baidu com article e2284b2b90c4dee2e6118dd3 html OBS直播软件是一款国外开发的用于网络直播的软件 本篇OBS教程主要介绍OBS下载和初级应用 工具 原料 一台
  • Open3D 最小二乘拟合空间直线(方法一)

    目录 一 算法原理 1 空间直线 2 最小二乘法拟合 二 代码实现 三 结果展示 本文由CSDN点云侠原创 原文链接 如果你不是在点云侠的博客中看到该文章 那么此处便是不要脸的爬虫 一 算法原理 1 空间直线 x
  • MySQL环境搭建利器---Sandbox

    MySQL环境搭建利器 Sandbox https metacpan org pod MySQL Sandbox http mysqlsandbox net https www cnblogs com gomysql p 3767445 h
  • Java中Arrays类的常用方法

    Java中Arrays类的常用方法 Arrays类位于 java util 包中 主要包含了操作数组的各种方法 import java util Arrays Arrays fill 填充数组 int arr new int 5 新建一个大
  • pycharm使用中的小tip

    1 双击shift会弹出全局搜索功能 关闭 双击shift 打开全局搜索 action gt registry 找到ide suppress dounle hangler勾上 重新打开 ctrl shift a 2 快速格式化代码 ctrl
  • pytorch 实现SSD详细理解 (一)vgg和特征图的提取

    摘要 本章就开始进入SSD的学习 通过学习这些基础的目标检测算法更好的对比理解其它算法 多看几种代码的写法更容易找到适合自己书写的套路 ssd网络的6个特征图 ssd采用的是vgg16的特征提取 在vgg16中提取二个特征图 之后又通过额外

随机推荐

  • Linux高性能I/O框架库Libevent介绍

    C C Linux服务器开发 后台架构师知识体系 这篇文章主要讲一下Libevent库的内容 顺便对I O库整体做个介绍 Linux服务器程序必须处理的三类事件 I O事件 信号 定时事件 在处理这三类事件时我们通常需要考虑如下三个问题 统
  • 解决Action和Servlet共存问题

    一 问题的提出 先让我们看下 web xml 中 struts2 和 servlet 的配置
  • 程序员的生活1

    记录最近生活 最近用C Winform DevExpress InstallShield 开发数据管理及分析软件 在懵懵懂懂的路上 越走越远 项目也差不多进行了一年 当然也会在后期 陆续更新项目上相关的东西 虽然界面很丑 但学习的热情不能因
  • matlab的gscale函数

    function g gscale f varargin if length varargin 0 method full8 else method varargin 1 end if strcmp class f double max f
  • selenium元素等待

    元素等待 js代码执行渲染期间 导致数据 标签加载缓慢 如果元素没有加载完成 直接通过find element去进行定位 会出现元素不存在的异常 3种方法 1 time sleep 10 强制性等待 不管元素在设置时间范围内是否加载出来 都
  • NOIP中的数学---第3课 约数

    约数定义 思考三秒钟 注意约数是成对出现的 如果数a能被数b整除 a就叫做 b 的倍数 b就叫做a的约数 约数和倍数都表示一个整数与另一个整数的关系 不能单独存在 最大公约数是指两个或多个整数共有约数中最大的一个 例如 24 18 共同的约
  • Redis缓存知识-穿透、击穿、雪崩

    目录 一 Redis介绍 二 Redis做缓存服务器 三 缓存穿透 击穿 雪崩 1 缓存穿透 2 缓存击穿 3 缓存雪崩 大家好 我是杨叔 每天进步一点点 关注我的微信公众号 程序员杨叔 获取更多测试开发技术知识 今天分享的内容是 Redi
  • 安卓(Android studio)编写一个计算器,可以连续加减乘除,可加小数点,不包含括号以及加减乘除优先算法。

    安卓 Android studio 编写一个计算器 可以累加 可加小数点 不包含括号以及加减乘除优先算法 首先需要在res目录下的layout文件下创建一个xml文件进行界面的布局设计 接着在Java目录下创建一个Java的class类文件
  • 折纸问题——打印折痕方向

    题目 请把一段纸条竖着放在桌子上 然后从纸条的下边向上方对折1次 压出折痕后展开 此时 折痕是凹下去的 即折痕突起的方向指向纸条的背面 如果从纸条的下边向上方连续对折2 次 压出折痕后展开 此时有三条折痕 从上到下依次是下折痕 下折痕和上折
  • Zabbix监控httpd服务

    Zabbix监控httpd服务 一 工作要求及目的 二 WEB配置步骤 1 进入监控主机列表 2 链接模板 3 查看自定义HTTP端口监控项 4 查看自定义的触发器 三 客户端测试http服务停止报警 1 停止服务 2 查看web端报警 3
  • 去除百度推广的广告

    在百度搜索过的关键字后 经常在其他网页看到百度的推广广告 超级烦人而且涉及隐私 下面是去除方法 C Windows System32 drivers etc下的hosts文件 加入 127 0 0 1 cpro baidu com 127
  • 理解目标检测当中的mAP

    我们在评价一个目标检测算法的 好坏 程度的时候 往往采用的是pascal voc 2012的评价标准mAP 网上一些资料博客参差不齐 缺乏直观易懂的正确说明 希望这篇博文能够给大家一点帮助 mAP历史 目标检测的mAP计算方式在2010年的
  • 【PBR系列二】辐射度量学理论

    本文核心知识主要参照 现代计算机图形学入门 闫令琪课程课件PPT 后续光线追踪系列辐射度量学知识也源于此处 辐射度量学理论 在开始之前我们先思考 为什么学习辐射度量学 辐射度量学是基于物理光照的基础 一 辐射能和辐射通量 辐射能 Radia
  • 员工管理系统服务器,员工管理系统小结 - 四七的个人空间 - OSCHINA - 中文开源技术交流社区...

    练习中出现 DAO层 Service层 Controler层 entity 层 utils 层 mapper 层 想知道几个之间的关系是如何 分层的目的是 高内聚 低耦合 DAO层 接口 也叫数据访问层 做的是数据持久层的工作 负责与数据库
  • Difference between RTL and Behavioral verilog

    原文链接 https electronics stackexchange com questions 63682 difference between rtl and behavioral verilog Answer 1 ehaviora
  • 详解KVM虚拟化原理

    详解KVM虚拟化原理 KVM架构 KVM Kernel based Virtual Machine 包含一个为处理器提供底层虚拟化 可加载的核心 模块kvm ko kvm intel ko或kvm amd ko 使用QEMU QEMU KV
  • sqlserver登录名和用户名的区别和联系-先存着-后续研究

    总括 登录名可以理解为进入整个大楼的钥匙 用户名可以理解为一个房间的钥匙 这里所说的大楼就是sql server服务器 而房间就是这个sql server服务器中的具体的库 要注意登录名是存在于master数据库的syslogins表中 用
  • 底部导航栏怎么写?

    底部导航栏需要怎么写 1 回忆一下 任何手机商城页面 底部导航栏都算固定在下面的 不管页面内容有多少 不管用户怎么滑动 底部导航栏始终在下面 2 点击到导航栏上的图标或者文字时 会跳转另一页面 3 点击导航栏上的图标或者文字时 所点的图标可
  • 爬取学校网站

    完整代码如下 可直接copy from bs4 import BeautifulSoup from bs4 import UnicodeDammit import urllib request import threading def im
  • 源码分析Hadoop FileInputFormat如何分片

    Hadoop采用的是分布式并行计算的模式来处理大数据 在处理时必然要对数据进行分片 将数据由大化小 将一个大的任务化为几个小的任务 这就是hadoop处理大数据的核心思想 这里要讨论的是hadoop对数据进行分片的方案 这里的分片是逻辑上的