知识学习——Hadoop InputFormat

来源:互联网 发布:语音视频聊天软件 编辑:程序博客网 时间:2024/05/21 09:44

MapReduce工作流程

这里写图片描述

(1)Hadoop中的Map Reduce框架依赖InputFormat提供数据,依赖OutputFormat输出数据,每一个Map Reduce程序都离不开它们。

(2)Map在处理文件之前,InputFormat接口的getSplits方法会将文件划分切割成为若干个可序列化的split。

(3)一般大多数的split与HDFS中的block大小相同,都为64M,这样做的好处是使得Map可以在存储有当前数据的节点上运行本地的任务,而不需要通过网络进行跨节点的任务调度。

(4)如果一个split大于一个block的时候,那么部分数据很可能存储在别的节点上,处理的时候必然会通过网络跨节点数据传输,则Map无疑是增加了等待时间从而降低了Map处理效率。

(5)如果一个split小于block的时候,那么会对当前节点block容量的浪费,同时是增加了split的个数,Map对split进行计算并且上报结果,关闭当前计算打开新的split均需要耗费资源,这样也降低了Map处理效率。

(6)一个大文件一般要划分为若干个split,因此,处理一个split的时间远远小于处理整个文件的时间。

(7)根据木桶效应,整个Map处理的速度则是由群集中所有运行map节点的最慢的那个节点决定。

(8)如果将splits分成较为细粒度的数据大小,而同时对不同的节点计算机根据其速度分配splits个数,可以获得更好的负载均衡。

InputFormat主要功能

InputFormat主要用于描述输入数据的格式,它提供以下两个功能

  • 数据切分:按照某个规则策略将输入的数据切分成若干个split,以便确定Map Task的个数以及对应的split

  • 为Mapper提供输入数据:给定某个split,能将其解析成一个个的key/value对

inputFormat包含两个方法

InputSplit[] getSplits(JobConf job,int numSplits) throws IOExceptionRecordReader<k,v> getRecordReader(InputSplit split,JobConf job,Reporter reporter) throws IOException;

1、getSplits方法

getSplits主要完成数据的切分功能,它将输入的数据切分成numSplits个inputSplit。inputSplit有两个特点:

1.逻辑切分:它在逻辑上切分输入数据,但不会在磁盘上将其切分存储,InputSplit只记录切分之后分片的元数据信息,比如起始位置长度以及所在节点列表等

2.可序列化:作业提交到JobTracker之前,Client会调用InputFormat中的getSplits方法,得到InputSplit并将其序列化到文件中。当JobTracker对提交上来的作业初始化的时候可以直接读取该文件,然后解析所有的InputSplit并且创建MapTask

2、getRecordReader方法

getRecordReader方法返回一个RecordReader对象,该对象将输入的InputSplit解析成若干个key/value对;

MapReduce框架在Map Task执行过程中,会不断地调用RecordReader对象中的方法,迭代获取key/value对并交给map函数处理。

InputFormat类层次结构图

这里写图片描述

基类FileInputFormat

基类FileInputFormat最重要的功能是为各种InputFormat提供统一的getSplits函数。该函数实现中最核心的两个算法是文件切分算法和host选择算法。

(1)文件切分算法

文件切分算法主要用于确定InputSplit的个数以及每个InputSplit对应的数据段。FileInputFormat以文件为单位切分生成InputSplit。对于每个文件,由以下三个属性值确定其对应的InputSplit的个数。

  • goalSize:它是根据用户期望的InputSplit数目计算出来的,即totalSize/numSplits。其中,totalSize为文件总大小;numSplits为用户设定的Map Task个数,默认情况下是1。

  • minSize:InputSplit的最小值,由配置参数mapred.min.split.size确定,默认是1。

  • blockSize:文件在HDFS中存储的block大小,不同文件可能不同,默认是64 MB。

上述三个参数共同决定InputSplit的最终大小,计算方法如下

  splitSize = max{minSize, min{goalSize, blockSize}} 

一旦确定splitSize值后,FileInputFormat将文件依次切成大小为splitSize的InputSplit,最后剩下不足splitSize的数据块单独成为一个InputSplit。

(2)host选择算法

待InputSplit切分方案确定后,下一步要确定每个InputSplit的元数据信息。

这通常由四部分组成:<file, start, length, hosts>,分别表示InputSplit所在的文件、起始位置、长度以及所在的host(节点)列表。

其中,前三项很容易确定,难点在于host列表的选择方法。

InputSplit的host列表选择策略直接影响到运行过程中的任务本地性。

在HDFS上,文件是以block为单位存储的,一个大文件对应的block可能遍布整个Hadoop集群,而InputSplit的划分算法可能导致一个InputSplit对应多个block ,这些block可能位于不同节点上,这使得Hadoop不可能实现完全的数据本地性。
Hadoop将数据本地性按照代价划分成三个等级:

  • node locality
  • rack locality
  • data center locality

在进行任务调度时,会依次考虑这3个节点的locality,即优先让空闲资源处理本节点上的数据,如果节点上没有可处理的数据,则处理同一个机架上的数据,最差情况是处理其他机架上的数据(但是必须位于同一个数据中心)

虽然InputSplit对应的block可能位于多个节点上,但考虑到任务调度的效率,通常不会把所有节点加到InputSplit的host列表中,而是选择包含(该InputSplit)数据总量最大的前几个节点(Hadoop限制最多选择10个,多余的会过滤掉),以作为任务调度时判断任务是否具有本地性的主要凭证。

为此,FileInputFormat设计了一个简单有效的启发式算法:

首先按照rack包含的数据量对rack进行排序,然后在rack内部按照每个node包含的数据量对node排序,最后取前N个node的host作为InputSplit的host列表,这里的N为block副本数。这样,当任务调度器调度Task时,只要将Task调度给位于host列表的节点,就认为该Task满足本地性。

这里写图片描述

从以上host选择算法可知,当InputSplit尺寸大于block尺寸时,Map Task并不能实现完全数据本地性,也就是说,总有一部分数据需要从远程节点上读取。因而可以得出以下结论:

当使用基于FileInputFormat实现InputFormat时,为了提高Map Task的数据本地性,应尽量使InputSplit大小与block大小相同。

InputFormat派生类

派生类实现getRecordReader函数,该函数返回一个RecordReader对象。它实现类似于迭代器的功能,将某个InputSplit解析成一个个<key,value>对。

实现时,RecordReader应考虑以下两点:

定位记录边界

为了能够识别一条完整的记录,记录之间应该添加一些同步标识。

对于TextInputFormat,每两条记录之间存在换行符;对于SequenceFileInputFormat,每隔若干条记录会添加固定长度的同步字符串。通过换行符或者同步字符串,可很容易定位到一个完整记录的起始位置。

由于FileInputFormat仅仅按照数据量多少对文件进行切分,因而InputSplit的第一条记录和最后一条记录可能会被从中间切开。为了解决这种记录跨越InputSplit的读取问题,RecordReader规定每个InputSplit的第一条不完整记录划给前一个InputSplit处理。

解析key/value

定位到一条新的记录后,需将该记录分解成key和value两部分。对于TextInputFormat,每一行的内容即为value,而该行在整个文件中的偏移量为key。

对于SequenceFileInputFormat,每条记录的格式为:

[record length] [key length] [key] [value] 

其中,前两个字段分别是整条记录的长度和key的长度,均为4字节,后两个字段分别是key和value的内容。知道每条记录的格式后,很容易解析出key和value。

常见的InputFormat派生类

TextInputFormat

作为默认的文件输入格式,用于读取纯文本文件,文件被分为一系列以LF或者CR结束的行,key是每一行的位置偏移量,是LongWritable类型的,value是每一行的内容,为Text类型。

KeyValueTextInputFormat

同样用于读取文件,如果行被分隔符(缺省是tab)分割为两部分,第一部分为key,剩下的部分为value;如果没有分隔符,整行作为 key,value为空。

SequenceFileInputFormat

用于读取sequence file。 sequence file是Hadoop用于存储数据自定义格式的binary文件。

它有两个子类:
SequenceFileAsBinaryInputFormat

将 key和value以BytesWritable的类型读出;

SequenceFileAsTextInputFormat

将key和value以Text类型读出。

NLineInputFormat

0.18.x新加入,可以将文件以行为单位进行split,比如文件的每一行对应一个map。得到的key是每一行的位置偏移量(LongWritable类型),value是每一行的内容,Text类型。

CompositeInputFormat

用于多个数据源的join。

DBInputFormat

从DB读取。