MapReduce输入格式

  文件是 MapReduce 任务数据的初始存储地。正常情况下,输入文件一般是存储在 HDFS 里面。这些文件的格式可以是任意的:我们可以使用基于行的日志文件, 也可以使用二进制格式,多行输入记录或者其它一些格式。这些文件一般会很大,达到数十GB,甚至更大。那么 MapReduce 是如何读取这些数据的呢?下面我们来学习 InputFormat 接口

1、InputFormat接口

  InputFormat接口决定了输入文件如何被 Hadoop分块(split up)与接受。InputFormat 能够从一个 job 中得到一个 split 集合(InputSplit[]),然后再为这个 split 集合配上一个合适的 RecordReader(getRecordReader)来读取每个split中的数据。 下面我们来看一下 InputFormat 接口由哪些抽象方法组成

2、InputFormat的抽象类方法

  InputFormat 包含两个抽象方法,如下所示

1 public abstract class InputFormat< K, V> {
2
3     public abstract List<InputSplit> getSplits(JobContext context) throws IOException,InterruptedException;
4
5     public abstract RecordReader<K,V> createRecordReader(InputSplit split,TaskAttemptContext context) throws IOException,InterruptedException;
7 }

  1)getSplits(JobContext context) 方 法负责将一个大数据逻辑分成许多片。比如数据库表有 100 条数据,按照主键ID升序存储。 假设每20条分成一片,这个List的大小就是5,然后每个InputSplit记录两个参数,第一个为这个分片的起始 ID,第二个为这个分片数据的大小,这里是20.很明显 InputSplit 并没有真正存储数据。只是提供了一个如何将数据分片的方法。

  2)createRecordReader(InputSplit split,TaskAttemptContext context)方 法根据 InputSplit 定义的方法,返回一个能够读取分片记录的 RecordReader。getSplit 用来获取由输入文件计算出来的 InputSplit, 后面会看到计算 InputSplit 时,会考虑输入文件是否可分割、文件存储时分块的大小和文件大小等因素;而createRecordReader() 提供了前面说的 RecordReader 的实现, 将Key-Value 对从 InputSplit 中正确读出来,比如LineRecordReader,它是以偏移值为Key,每行的数据为 Value,这使所有 createRecordReader() 返回 LineRecordReader 的 InputFormat 都是以偏移值为Key,每行数据为 Value 的形式读取输入分片的。

  其实很多时候并不需要我们实现 InputFormat 来读取数据,Hadoop 自带有很多数据输入格式,已经实现了 InputFormat接口

3、InputFormat接口实现类

  InputFormat 接口实现类有很多,其层次结构如下图所示

  

  1、FileInputFormat

    FileInputFormat是所有使用文件作为其数据源的 InputFormat 实现的基类,它的主要作用是指出作业的输入文件位置。因为作业的输入被设定为一组路径, 这对指定作业输入提供了很强的灵活性。FileInputFormat 提供了四种静态方法来设定 Job 的输入路径:

1 public static void addInputPath(Job job,Path path);
2
3 public static void addInputPaths(Job job,String commaSeparatedPaths);
4
5 public static void setInputPaths(Job job,Path... inputPaths);
6
7 public static void setInputPaths(Job job,String commaSeparatedPaths);

  addInputPath()、addInputPaths()方法可以将一 个或多个路径加入路径列表,可以分别调用这两种方法来建立路径列表;setInputPaths()方法一次设定完整的路径列表,替换前面调用中在 Job 上所设置的所有路径。它们具体的使用方法,看如下示例

 1     // 设置一个源路径
 2     FileInputFormat.addInputPath(job, new Path("hdfs://ljc:9000/buaa/inputPath1"));
 3
 4     // 设置多个源路径,多个源路径之间用逗号分开
 5     FileInputFormat.addInputPaths(job, " hdfs://ljc:9000/buaa/inputPath1, hdfs://ljc:9000/buaa/inputPath2,...");
 6
 7     // inputPaths是一个Path类型的数组,可以包含多个源路径,比如hdfs://ljc:9000/buaa/inputPath1,hdfs://ljc:9000/buaa/inputPath2,等
 8     FileInputFormat.setInputPaths(job, inputPaths);
 9
10     // 设置多个源路径,多个源路径之间用逗号分开
11     FileInputFormat.setInputPaths(job, " hdfs://ljc:9000/buaa/inputPath1, hdfs://ljc:9000/buaa/inputPath2,...");

  add 方法、set 方法允许指定包含的文件。如果需要排除特定文件,可以使用 FileInputFormat 的 setInputPathFilter()方法设置一个过滤器

1 public static void setInputPathFilter(Job job,Class<? extends PathFilter filter);

  关于过滤器,这里不再深入讨论。即使不设置过滤 器,FileInputFormat 也会使用一个默认的过滤器来排除隐藏文件。 如果通过调用 setInputPathFilter()设置了过滤器,它会在默认过滤器的基础上进行过滤。换句话说,自定义的过滤器只能看到非隐藏文件

  对于输入的数据源是文件类型的情况下,Hadoop 不仅擅长处理非结构化文本数据,而且可以处理二进制格式的数据, 但它们的基类都是FileInputFormat。下面我们介绍的几种常用输入格式,都实现了FileInputFormat基类

    1、TextInputFormat

    TextInputFormat 是默认的 InputFormat。每条记录是一行输入。键是LongWritable 类型,存储该行在整个文件中的字节偏移量。 值是这行的内容,不包括任何行终止符(换行符、回车符),它被打包成一个 Text 对象。

    比如,一个分片包含了如下5条文本记录,记录之间使用tab(水平制表符)分割

    1    22

    2    17

    3    17

    4    11

    5    11

    每条记录表示为以下键/值对:

    (0, 1    22)

    (5, 2    17)

    (10,3    17)

    (15,4    11)

    (20,5    11)

    很明显,键并不是行号。一般情况下,很难取得行号,因为文件按字节而不是按行切分为分片。

    2、KeyValueTextInputFormat

    每一行均为一条记录, 被分隔符(缺省是tab(\t))分割为key(Text),value(Text)。可以通过 mapreduce.input.keyvaluelinerecordreader.key.value,separator属性(或者旧版本 API 中的 key.value.separator.in.input.line)来设定分隔符。 它的默认值是一个制表符。

    比如,一个分片包含了如下5条文本记录,记录之间使用tab(水平制表符)分割。

    1    22

    2    17

    3    17

    4    11

    5    11

    每条记录表示为以下键/值对:

    (1,22)

    (2,17)

    (3,17)

    (4,11)

    (5,11)

    此时的键是每行排在制表符之前的 Text 序列。

    3、NLineInputFormat

    通过 TextInputFormat 和 KeyValueTextInputFormat,每个 Mapper 收到的输入行数不同。行数取决于输入分片的大小和行的长度。 如果希望 Mapper 收到固定行数的输入,需要将 NLineInputFormat 作为 InputFormat。与 TextInputFormat 一样, 键是文件中行的字节偏移量,值是行本身。N 是每个 Mapper 收到的输入行数。N 设置为1(默认值)时,每个 Mapper 正好收到一行输入。 mapreduce.input.lineinputformat.linespermap 属性(在旧版本 API 中的 mapred.line.input.format.linespermap 属性)实现 N 值的设定。

    以下是一个示例,仍然以上面的4行输入为例。

    1    22

    2    17

    3    17

    4    11

    5    11

    例如,如果 N 是3,则每个输入分片包含三行。一个 mapper 收到三行键值对:

    1    22

    2    17

    3    17

    另一个 mapper 则收到后两行(因为总共才5行,所有另一个mapper只能收到两行)

    4    11

    5    11

    这里的键和值与 TextInputFormat 生成的一样。

    4、SequenceFileInputFormat

    用于读取 sequence file。键和值由用户定义。序列文件为 Hadoop专用的压缩二进制文件格式。它专用于一个 MapReduce作业和其它 MapReduce作业之间的传送数据(适用与多个 MapReduce 链接操作)。

  2、多个输入

    虽然一个 MapReduce 作业的输入可以包含多个输入文件,但所有文件都由同一个 InputFormat 和 同一个 Mapper 来解释。 然而,数据格式往往会随时间演变,所以必须写自己的 Mapper 来处理应用中的遗留数据格式问题。或者,有些数据源会提供相同的数据, 但是格式不同。

    这些问题可以使用 MultipleInputs 类来妥善处理,它允许为每条输入路径指定 InputFormat 和 Mapper。例如,我们想把英国 Met Office 的气象站数据和 NCDC 的气象站数据放在一起来统计平均气温,则可以按照下面的方式来设置输入路径。

1 MultipleInputs.addInputPath(job,ncdcInputPath,TextInputFormat.class,NCDCTemperatureMapper.class);
2
3 MultipleInputs.addInputPath(job,metofficeInputPath,TextInputFormat.class,MetofficeTemperatureMapper.class);

    这段代码取代了对 FileInputFormat.addInputPath()和job.setMapperClass() 的常规调用。Met Office 和 NCDC 的数据都是文本文件,所以对两者都使用 TextInputFormat 数据类型。 但这两个数据源的行格式不同,所以我们使用了两个不一样的 Mapper,分别为NCDCTemperatureMapper和MetofficeTemperatureMapper。重要的是两个 Mapper 的输出类型一样,因此,reducer 看到的是聚集后的 map 输出,并不知道这些输入是由不同的 Mapper 产生的。

    MultipleInputs 类还有一个重载版本的 addInputPath() 方法,它没有 Mapper参数。如果有多种输入格式而只有一个 Mapper(通过 Job 的 setMapperClass()方法设定),这种方法很有用。其具体方法如下所示。

1 public static void addInputPath(Job job,Path path,class< ? extends InputFormat> inputFormatClass);

  3、DBInputFormat

    这 种输入格式用于使用 JDBC 从关系数据库中读取数据。因为它没有任何共享能力,所以在访问数据库的时候必须非常小心,在数据库中运行太多的 mapper 读数据可能会使数据库受不了。 正是由于这个原因,DBInputFormat 最好用于加载少量的数据集。与之相对应的输出格式是DBOutputFormat,它适用于将作业输出数据(中等规模的数据)转存到数据库

  4、自定义 InputFormat

    有时候 Hadoop 自带的输入格式,并不能完全满足业务的需求,所以需要我们根据实际情况自定义 InputFormat 类。而数据源一般都是文件数据,那么自定义 InputFormat时继承 FileInputFormat 类会更为方便,从而不必考虑如何分片等复杂操作。 自定义输入格式我们分为以下几步:

    1、继承 FileInputFormat 基类。

    2、重写 FileInputFormat 里面的 isSplitable() 方法。

    3、重写 FileInputFormat 里面的 createRecordReader()方法。

    按照上述步骤如何自定义输入格式呢?下面我们通过一个示例加强理解。

    我们取有一份学生五门课程的期末考试成绩数据,现在我们希望统计每个学生的总成绩和平均成绩。 样本数据如下所示,每行数据的数据格式为:学号、姓名、语文成绩、数学成绩、英语成绩、物理成绩、化学成绩

    19020090040 秦心芯 123 131 100 95 100

    19020090006 李磊 99 92 100 90 100

    。。。。。

    下面我们就编写程序,实现自定义输入并求出每个学生的总成绩和平均成绩。分为以下几个步骤,这里只给出步骤,代码见下

    第一步:为了便于每个学生学习成绩的计算,这里我们需要自定义一个 ScoreWritable类实现 WritableComparable 接口,将学生各门成绩封装起来

    第二步:自定义输入格式 ScoreInputFormat类,首先继承 FileInputFormat,然后分别重写 isSplitable() 方法和 createRecordReader() 方法。 需要注意的是,重写createRecordReader()方法,其实也就是重写其返回的对象ScoreRecordReader。 ScoreRecordReader 类继承 RecordReader,实现数据的读取

    第三步:编写 MapReduce 程序,统计学生总成绩和平均成绩。需要注意的是,上面我们自定义的输入格式ScoreInputFormat,需要在 MapReduce 程序中做如下设置,job.setInputFormatClass(ScoreInputFormat.class);//设置自定义输入格式

    一般情况下,并不需要我们自定义输入格式,Hadoop 自带有很多种输入格式,基本满足我们工作的需要

时间: 2024-08-10 23:30:12

MapReduce输入格式的相关文章

hadoop自定义输入格式

一个任务的开始阶段是由InputFormat来决定的! 1.在MapReduce框架中,InputFormat扮演的角色:– 将输入数据切分成逻辑的分片(Split),一个分片将被分配给一个单独的Mapper– 提供RecordReader的对象,该对象会从分片中读出<Key-Value>对供Mapper处理 1.1InputFormat对Mapper的影响:– 决定了Mapper的数量– 决定了Mapper的map函数接收的Key和Value 1.2InputFormat: InputFor

有五个学生,每个学生有3门课(语文、数学、英语)的成绩, * 写一个程序接收从键盘输入学生的信息,输入格式为:name,30,30,30(姓名,三门课成绩)

/* * 3. 有五个学生,每个学生有3门课(语文.数学.英语)的成绩, * 写一个程序接收从键盘输入学生的信息,输入格式为:name,30,30,30(姓名,三门课成绩), * 然后把输入的学生信息按总分从高到低的顺序写入到一个名称"stu.txt"文件中. * 要求:stu.txt文件的格式要比较直观,打开这个文件,就可以很清楚的看到学生的信息 * */ import java.io.*; import java.util.*; public class Test3 { publi

C# 各种输入格式验证#各种输入格式验证

/// <summary> /// 各种输入格式验证 /// </summary> public class ValidateUtil { private static Regex RegNumber = new Regex("^[0-9]+$"); private static Regex RegNumberSign = new Regex("^[+-]?[0-9]+$"); private static Regex RegDecimal

三道习题(1、将单词表中由相同字母组成的单词归成一类,每类单词按照单词的首字母排序,并按 #每类中第一个单词字典序由大到小排列输出各个类别。 #输入格式:按字典序由小到大输入若干个单词,每个单词占一行,以end结束输入。)

#coding=gbk ''' 1.将单词表中由相同字母组成的单词归成一类,每类单词按照单词的首字母排序,并按 #每类中第一个单词字典序由大到小排列输出各个类别. #输入格式:按字典序由小到大输入若干个单词,每个单词占一行,以end结束输入. #cinema #iceman #maps #spam #aboard #abroad #end #输出格式:一类单词一行,类别间单词以空格隔开. #aboard abroad #cinema iceman #maps spam ''' result=[]

[python]特殊输入格式(输入中包含空格逗号等)

特殊输入格式 输入中包含空格 a,b=map(int,input().split()) a,b,c=map(int,input().split()) 输入中包含 '/' a,b=map(int,input().split('/')) 输入中包含',' a,b=map(int,input().split(',')) 原文地址:https://www.cnblogs.com/xzzheng/p/10316428.html

C++学习 | 通过正则表达式保证输入格式的正确性

--仅记录个人学习过程-- 如果题目中提出了"读入特定输入格式的数据,如果不符合要求则抛出异常.输出错误提示.结束程序"的要求,使用C++的普通输入流无法解决.我使用的方法是逐行读入.结合正则表达式. 题目要求: 从input.txt文件中读入以下内容 首先输入一个正整数n,表示接下来输入的数据总行数. 输入n行数据,每行形如"C 0 1 3"或"L 2 3 5 3"或"S 3 6 4 1"(字符'C'后跟三个整数,或字符'L

C语言scanf输入格式 printf输出格式

对于刚接触C语言的同学来说 可能会遇到这个问题 因为博主也是个菜鸟经常因为搞不清楚单精度双精度还有输入输出格式的问题 就这个问题也百度的很多次 所以在这里总结一下分享给大家 printf输出格式 1.转换说明符       %a(%A)     浮点数.十六进制数字和p-(P-)记数法(C99)       %c             字符       %d             有符号十进制整数       %f              浮点数(包括float和doulbe)      

一个简单的日期输入格式化控件。

js代码有一百多行. 先上效果图 html代码 日期: <input type="text" id="dateInputer" class="hhm-dateInputer" placeholder="请输入日期"> 设置input元素类名为 hhm-dateInputer,通过这个类来绑定这个日期输入控件. js代码 这里应用了jQuery的库, 主要用于选择元素和绑定事件. <script src=&qu

openCV打开图像或视频时绝对路径输入格式

在openCV中打开图像或视频的方法有多种,最常用的有 1.只需输入文件名和后缀名 当需要打开的文件存放在工程文件下的test文件夹下时,只需输入文件名和后缀名,博主在这提醒初学者,一定注意后缀名问题,我已吃过很多次亏了,一个有效的办法是加一个验证过程,如下代码所示: Mat image = imread("pp.jpg",0); if (image.empty()) { cout << "error"; return -1; } cout <&l