3MapReduce编程.docx
《3MapReduce编程.docx》由会员分享,可在线阅读,更多相关《3MapReduce编程.docx(26页珍藏版)》请在冰豆网上搜索。
3MapReduce编程
MapReduce编程
1、实验目的
1、理解MapReduce编程模型基本知识
2、掌握MapReduce开发环境的搭建
3、掌握MapReduce基本知识,能够运用MapReduce进行基本的开发
2、实验原理
MapReduce是Hadoop两个最基础最重要的核心成员之一。
它是大规模数据(TB级)计算的利器,Map和Reduce是它的主要思想,来源于函数式编程语言。
从编程的角度来说MapReduce分为Map函数和Reduce函数,Map负责将数据打散,Reduce负责对数据进行聚集,用户只需要实现map和reduce两个接口,即可完成TB级数据的计算。
HadoopMapReduce的实现采用了Master/Slave结构。
Master叫做JobTracker,而Slave叫做TaskTracker。
用户提交的计算叫做Job,每一个Job会被划分成若干个Tasks。
JobTracker负责Job和Tasks的调度,而TaskTracker负责执行Tasks。
常见的应用包括:
日志分析和数据挖掘等数据分析应用,另外,还可用于科学数据计算,如圆周率PI的计算等。
MapReduce框架的核心步骤主要分两部分:
Map和Reduce。
当你向MapReduce框架提交一个计算作业时,它会首先把计算作业拆分成若干个Map任务,然后分配到不同的节点上去执行,每一个Map任务处理输入数据中的一部分,当Map任务完成后,它会生成一些中间文件,这些中间文件将会作为Reduce任务的输入数据。
Reduce任务的主要目标就是把前面若干个Map的输出汇总到一起并输出。
按照以上基本的描述,其工作图如下。
从工作流程来讲,MapReduce对应的作业Job首先把输入的数据集切分为若干独立的数据块,并由Map组件以Task的方式并行处理。
处理结果经过排序后,依次输入给Reduce组件,并且以Task的形式并行处理。
MapReduce对应的输入输出数据由HDFS的DataNode存储。
MapReduce对应的Job部署在Master服务器,由MasterJobTracker负责Task的调度,监控,重新执行失败的任务等等。
MapReduce对应的Job部署在若干不同的Slave服务器,每个集群节点含一个slaveTaskTracker,负责执行由master指派的任务。
从高层抽象来看,MapReduce的数据流图如下图所示:
3、实验内容
1、MapReduce计算模型描述
2、MapReduce编程基本知识
3、MapReduce开发环境的搭建
4、MapReduce实例开发演练
4、实验步骤
本实验以unbutu14.04,hadoop2.20集群为前提进行实验。
Hadoop集群ip及角色分配如下
10.31.44.117master(namenode)
10.31.44.200slaver1(datanode)
10.31.44.201slaver2(datanode)
第一部分:
MapReduce计算模型描述
对于mapreduce编程模型,引用一个个人认为比较经典的图片来说明问题.
如上图所示:
1.首先,我们能确定我们有一份输入,而且他的数据量会很大
2.通过split之后,他变成了若干的分片,每个分片交给一个Map处理
3.map处理完后,tasktracker会把数据进行复制和排序,然后通过输出的key和value进行partition的划分,并把partition相同的map输出,合并为相同的reduce的输入.
4.ruducer通过处理,把数据输出,每个相同的key,一定在一个reduce中处理完,每一个reduce至少对应一份输出(可以通过扩展MultipleOutputFormat来得到多分输出)
5.来看一个例子,如下图:
说明几点:
5.1输入的数据可能就是一堆文本
5.2mapper会解析每行数据,然后提取有效的数据,作为输出.这里的例子是从日志文件中提取每一年每天的气温,最后会计算每年的最高气温
5.3map的输出就是一条一条的key-value
5.4通过shuffle之后,变成reduce的输入,这是相同的key对应的value被组合成了一个迭代器
5.5reduce的任务是提取每一年的最高气温,然后输出
第二部分:
MapReduce编程基本知识
特别数据类型介绍
Hadoop提供了如下内容的数据类型,这些数据类型都实现了WritableComparable接口,以便用这些类型定义的数据可以被序列化进行网络传输和文件存储,以及进行大小比较。
BooleanWritable:
标准布尔型数值
ByteWritable:
单字节数值
DoubleWritable:
双字节数
FloatWritable:
浮点数
IntWritable:
整型数
LongWritable:
长整型数
Text:
使用UTF8格式存储的文本
NullWritable:
当中的key或value为空时使用
InputFormat和InputSplit:
InputSplit是Hadoop定义的用来传送给每个单独的map的数据,InputSplit存储的并非数据本身而是一个分片长度和一个记录数据位置的数组。
生成InputSplit的方法可以通过InputFormat()来设置。
OutputFormat:
每一种输入格式都有一种输出格式与其对应。
默认的输出格式是TextOutputFormat,这种输出方式与输入类似,会将每条记录以一行的形式存入文本文件。
不过,它的键和值可以是任意形式的,因为程序内容会调用toString()方法将键和值转换为String类型再输出。
特别类介绍
Mapper
1、mapper可以选择性地继承MapreduceBase这个基类,他只是把一些方法实现了而已,即使方法体是空的.
2、mapper必须实现Mapper接口(0.20以前的版本),这是一个泛型接口,需要执行输入和输出的key-value的类型,这些类型通常都是Wriable接口的实现类
3、实现map方法,方法有四个参数,前面两个就是输入的Key和value,第三个参数是OuputCollector,用于收集输出的,第四个是reporter,用来报告一些状态的,可以用于debug
3.1input默认是一行一条记录,每天记录都放在value里边
3.2output每次搜集一条K-V记录,一个K可以对应多个value,在reduce里面体现为一个iterator
4、覆盖configure方法可以得到JobConf的实例,这个JobConf是在Job运行时传递过来的,可以跟外部资源进行数据交互
Reducer
1、reduce也可以选择继承MapreduceBase这个基类,功能跟mapper一样.
2.、reducer必须实现Reducer接口,这个接口同样是泛型接口,意义跟Mapper的类似
3、实现reduce方法,这个方法也有四个参数,第一个是输入的key,第二个是输入的value的迭代器,可以遍历所有的value,相当于一个列表,
outputCollector跟map的一样,是输出的搜集器,每次搜集都是key-value的形式,report的作用跟map的相同.
4、在新版本中,hadoop已经将后面两个参数合并到一个context对象里边了,当然还会兼容就版本的接口.>0.19.x
5、覆盖configure方法,作用跟map的相同
6、覆盖close方法,可以做一些reduce结束后的处理工作.(cleanup)
Combiner
1、combiner的作用是,将map的输出,先计算一遍,得到初步的合并结果,减少reduce的计算压力.
2、combiner的编写方法跟reduce是一样的,他本来就是一个Reducer的实现类
3、当reducer符合函数F(a,b)=F(F(a),F(b))时,combinner可以与reduce相同.比如sum(a,b,c,d,e,f,g)=sum(sum(a,b),sum(c,d,e,f),sum(g))还有max,min等等.
4、编写正确的combiner可以优化整个mapreduce程序的性能.(特别是当reduce是性能瓶颈的时候.)
5、combiner可以跟reducer不同.
Configuration
1、后加的属性的值会覆盖前面定义的相同名称的属性的值.
2、被定义为final的属性(在属性定义中加上true标签)不会被后面的同名属性定义的值给覆盖.
3、系统属性比通过资源定义的属性优先级高,也就是通过System.setProperty()方法会覆盖在资源文件中定义的属性的值.
4、系统属性定义必须在资源文件中有相应的定义才会生效.
5、通过-D选项定义的属性,比在资源文件中定义的属性优先级要高.
RunJobs
1、设置inputs&output
1.1先判断输入是否存在(不存在会导致出错,最好利用程序来判断.)
1.2判断输出是否已经存在(存在也会导致出错)
1.3养成一种好的习惯(先判断,再执行)
2、设置mapper、reducer、combiner.各个实现类的class对象.XXXX.class
3、设置inputformat&outputformat&types
3.1input和outputformat都有两种,一种是textfile,一种是sequencefile.简单理解,textfile是文本组织的形式,sequencefile是二进制组织的形式.
3.2Types的设置,根据输入和输出的数据类型,设置各种Writable接口的实现类的class对象.
4、设置reducecount
4.1reducecount可以为0,当你的数据无需reduce的时候.
4.2reduce数量最好稍微少于当前可用的slots的数量,这样reduce就能在一波计算中算好.(一个slot可以理解为一个计算单元(资源).)
第三部分:
MapReduce开发环境的搭建
在此主要介绍在master服务器下,使用eclipse进行MapReduce开发
首先安装eclipse命令如下:
apt-getinstalleclipse
下载eclipse开发hadoop2.20MapReduce插件hadoop-eclipse-plugin-2.2.0.jar
提供一个网址免费下载链接:
将下载好的插件放入eclipse安装目录下的dropins文件夹中。
放置好之后,重启eclipse,按照1、2、3进行操作,如下图
紧接着如下图进行如下操作:
以上步骤完成后,会在eclipse左边出现DFSXX目录可以进行如下操作:
此时将可以看到HDFS文件存储结构,并可以尝试进行上传,下载创建等操作,如能正常操作则至此HadoopMapReduce开发平台搭建完毕。
下面创建做一个demo工程看看是否能正常创建MapReduce工程。
第四部分:
MapReduce实例开发演练
基于第三部分,MapReduce开发环境的搭建完成,下面做以下实验来具体说明MapReduce编程思想、开发流程。
1、数据去重
数据去重"主要是为了掌握和利用并行化思想来对数据进行有意义的筛选。
统计大数据集上的数据种类个数、从网站日志中计算访问地等这些看似庞杂的任务都会涉及数据去重。
下面就进入这个实例的MapReduce程序设计。
1.1实例描述
对数据文件中的数据进行去重。
数据文件中的每行都是一个数据。
dedup1.txt,dedup2.txt为上传至HDFS上的文件(可以通过eclipse新建input文件夹,然后上传至此目录下)样例输入内容如下所示:
1)dedup1.txt:
2)dedup2.txt:
经过MapReduce去重之后,样例输出如下所示:
1.2、设计思路
数据去重的最终目标是让原始数据中出现次数超过一次的数据在输出文件中只出现一次。
我们自然而然会想到将同一个数据的所有记录都交给一台reduce机器,无论这个数据出现多少次,只要在最终结果中输出一次就可以了。
具体就是reduce的输入应该以数据作为key,而对value-list则没有要求。
当reduce接收到一个时就直接将key复制到输出的key中,并将value设置成空值。
(reduce中的key表示的是我们要统计的数据例如2012-3-7c,另外的value可以理解为一个序号,没有太大的作用,可理解为无意义数据)
在MapReduce流程中,map的输出经过shuffle过程聚集成后会交给reduce。
shuffle是MapReduce的关键,也是mapreduce的难点,明白了shuffle,mapreduce就没有什么内容了。
而这里的value-list则是shuffle的难点。
value-list可以理解是用来标识有效数据的。
但是其本身没有太大意义。
所以从设计好的reduce输入可以反推出map的输出key应为数据,value任意。
继续反推,map输出数据的key为数据,而在这个实例中每个数据代表输入文件中的一行内容,所以map阶段要完成的任务就是在采用Hadoop默认的作业输入方式之后,将value设置为key,并直接输出(输出中的value任意)。
map中的结果经过shuffle过程之后交给reduce。
reduce阶段不会管每个key有多少个value,它直接将输入的key复制为输出的key,并输出就可以了(输出中的value被设置成空了)。
1.3、程序代码
程序代码如下所示:
packagecom.dedup.main;
importjava.io.IOException;
importorg.apache.hadoop.conf.Configuration;
importorg.apache.hadoop.fs.Path;
importorg.apache.hadoop.io.Text;
importorg.apache.hadoop.mapreduce.Job;
importorg.apache.hadoop.mapreduce.Mapper;
importorg.apache.hadoop.mapreduce.Reducer;
importorg.apache.hadoop.mapreduce.lib.input.FileInputFormat;
importorg.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
importorg.apache.hadoop.util.GenericOptionsParser;
publicclassDedup{
//map将输入中的value复制到输出数据的key上,并直接输出
publicstaticclassMapextendsMapper
privatestaticTextline=newText();//每行数据
//实现map函数
publicvoidmap(Objectkey,Textvalue,Contextcontext)
throwsIOException,InterruptedException{
line=value;
context.write(line,newText(""));
}
}
//reduce将输入中的key复制到输出数据的key上,并直接输出
publicstaticclassReduceextendsReducer{
//实现reduce函数
publicvoidreduce(Textkey,Iterablevalues,Contextcontext)
throwsIOException,InterruptedException{
context.write(key,newText(""));
}
}
publicstaticvoidmain(String[]args)throwsException{
Configurationconf=newConfiguration();
//这句话很关键
conf.set("mapred.job.tracker","10.31.44.117:
9001");
String[]ioArgs=newString[]{"hdfs:
//10.31.44.117:
9000/input/dedup*",
"hdfs:
//10.31.44.117:
9000/output/dedupout"};
String[]otherArgs=newGenericOptionsParser(conf,ioArgs).getRemainingArgs();
if(otherArgs.length!
=2){
System.err.println("Usage:
DataDeduplication");
System.exit
(2);
}
Jobjob=newJob(conf,"Dedup");
job.setJarByClass(Dedup.class);
//设置Map、Combine和Reduce处理类
job.setMapperClass(Map.class);
job.setCombinerClass(Reduce.class);
job.setReducerClass(Reduce.class);
//设置输出类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
//设置输入和输出目录
FileInputFormat.addInputPath(job,newPath(otherArgs[0]));
FileOutputFormat.setOutputPath(job,newPath(otherArgs[1]));
System.exit(job.waitForCompletion(true)?
0:
1);
}
}
1.4运行过程及结果
运行配置如下图:
运行结果如下:
2、数据排序
2.1简介:
"数据排序"是许多实际任务执行时要完成的第一项工作,比如学生成绩评比、数据建立索引等。
这个实例和数据去重类似,都是先对原始数据进行初步处理,为进一步的数据操作打好基础。
下面进入此实验。
2.2实例描述
对输入文件中数据进行排序。
输入文件中的每行内容均为一个数字,即一个数据。
要求在输出中每行有两个间隔的数字,其中,第一个代表原始数据在原始数据集中的位次,第二个代表原始数据。
下面sort1.txt,sort2.txt,sort3.txt均为上传至HDFS文件系统下的/input目录下的文件。
样例输入:
1)sort1.txt:
2)sort2.txt:
3)sort3.txt:
预想结果样例输出:
2.3设计思路
此实验仅仅要求对输入数据进行排序,熟悉MapReduce过程的读者会很快想到在MapReduce过程中就有排序,是否可以利用这个默认的排序,而不需要自己再实现具体的排序呢?
答案是肯定的。
但是在使用之前首先需要了解它的默认排序规则。
它是按照key值进行排序的,如果key为封装int的IntWritable类型,那么MapReduce按照数字大小对key排序,如果key为封装为String的Text类型,那么MapReduce按照字典顺序对字符串排序。
了解了这个细节,我们就知道应该使用封装int的IntWritable型数据结构了。
也就是在map中将读入的数据转化成IntWritable型,然后作为key值输出(value任意)。
reduce拿到之后,将输入的key作为value输出,并根据value-list中元素的个数决定输出的次数。
输出的key(即代码中的linenum)是一个全局变量,它统计当前key的位次。
需要注意的是这个程序中没有配置Combiner,也就是在MapReduce过程中不使用Combiner。
这主要是因为使用map和reduce就已经能够完成任务了。
2.4程序代码
程序代码如下所示:
packagecom.sort.main;
importjava.io.IOException;
importorg.apache.hadoop.conf.Configuration;
importorg.apache.hadoop.fs.Path;
importorg.apache.hadoop.io.IntWritable;
importorg.apache.hadoop.io.Text;
importorg.apache.hadoop.mapreduce.Job;
importorg.apache.hadoop.mapreduce.Mapper;
importorg.apache.hadoop.mapreduce.Reducer;
importorg.apache.hadoop.mapreduce.lib.input.FileInputFormat;
importorg.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
importorg.apache.hadoop.util.GenericOptionsParser;
publicclassSort{
//map将输入中的value化成IntWritable类型,作为输出的key
publicstaticclassMapextends
Mapper
privatestaticIntWritabledata=newIntWritable();
//实现map函数
publicvoidmap(Objectkey,Textvalue,Contextcontext