目录
6.1MapReduce概述
6.1.1MapReduce的特点
6.1.2MapRedcue的应用场景
6.2MapReduce执行过程
6.2.1单词统计实例
6.2.2MapReduce执行过程
6.2.3Map Reduce的文件切片---Split
6.2.4Map过程和Reduce过程
6.2.5Shuffle过程
6.3MapReduce实例
(1)本机配置Hadoop环境变量
(2)放置winutil.exe文件和hadoop.dll
(3)新建一个Java工程,导入需要的JAR包。新建一个WordCountMapper类
(4)新建一个WordCountReducer类
(5)创建主方法:
(6)创建处理文件
(7)运行结果
常见问题汇总:
Hadoop中有两个重要的组件,一个是HDFS,另一个是MapReduce,HDFS用来存储大批量得数据,而MapReduce则是通过计算来发现数据中有价值得内容。
hadoop作为开源组织下最重要得项目之一,最推出后得到了全球学术界和工业界的广泛关注、推广和普及。它是开源项目Lucene(搜索索引程序库)和Nutch(搜索引擎)的创始人Doug Cutting于2004年推出的,当时Doug Cutting 发现MapReduce正是其所需要解决大规模Web数据处理的重要计数,而因模仿Goole MapReduce,基于Java设计开发了一个成为hadoop的开源MapReduce并行计算框架和系统。
我们前面说过hadoop的HDFS用于存储数据,MapReduce用来计算数据。接着来介绍一下MapReduce的特点。MapReduce适合处理离线的海量数据,这里的“离线”可以理解为本地存储,非实时处理。李先计算旺旺需要一段时间,如几分钟或者几个小时们根据业务数据和业务复杂程度有所区别。MapReduce往往处理大批数据,比如PB级或者AB级别。特点如下
易于编程:如果要编写分布式程序,只需要实现一些简单的接口,与编写普通程序类似,避免了复杂的过程。同时,编写的这个分布式程序可以部署到大批廉价的普通机器上运行。
具有良好的拓展性:是指当一台机器的计算资源不能满足存储或者计算的时候,可以通过增加机器来增加存储和计算能力。
具有高容错性:MapReduce设计的初衷是可以使程序部署运行在链家的机器上,廉价的机器坏的概率相对较高,这就要求其具有良好的容错性。当一台机器“挂掉”以后,相应数据的存储和计算能力会被一直到另外一台机器上,从而实现容错性。
MapReduce的应用场景主要变现在从大规模数据中进行计算,不要求即时返回结果的场景,比如以下典型应用:
单词统计
简单的数据统计,如网站PV和UV统计
搜索引擎建立索引
搜索引擎中,统计最流行的K个搜索词
统计搜索词频率,帮助优化搜索词提示
复杂数据分析算法实现
下面介绍MapReduce不适用的方面:
实时计算,MapReduce不适合在毫秒级或者秒级内返回结果。
流式计算,MapReduce的输入数据是静态的,不能动态变化,所以不适合流式计算。
DAG计算,如果多个应用程序存在一栏关系,并且后一个应用程序的输入为前一个的输出,这种情况也不适合。
假设有一个非常大的文件,需求是统计文件中每个单词的出现的次数,MapReduce的执行过程如下图
图中主要分为Split、Map、Shuffle、和Reduce阶段,每个阶段在WordCount中的作用如下:
Split阶段:首先大文件被切成多分,假设这里被切成了3份,每一行代表一份
Map阶段:解析出每个单词,并在后面机上数字1
Shuffle阶段:将每一份中的单词分组到一起,并按照字母进行排序
Reduce阶段,将相同的单词进行累加
输出结果
从WrodCount实例中,可以给予单词统计大概了解列MapReduce过程。接下来我们从理论层面面介绍一下
具体执行过程树下:
(1)数据会被切割成数据片段
(2)数据片段以key和value的形式被读进来,默认是以行的下标位作为key,以行的内容作为value
(3)数据会传入Map中进行处理,处理逻辑由用户自行定义,在Map中处理完后还是以key和value的形式输出。
(4)输出的数据传给列Shuffle(洗牌),Shuffle完成对数据的排序和合并等操作,但是Shuffle不会对输入的数据进行改动,所以还是key2、value2
(5)数据随后传给Reduce进行处理,Reduce处理完成后,生成key3、value3
(6)Reduce处理完成后数据会被写到HDFS某个目录
Split的大小默认与 block对应,也可以由用户任意控制。MapReduce的SPlit大小计算公式如下:
max(min.split,min(max.split,block))
其中,max.split = totalSize/numSplit,totalSplit为文件大小,numSplit为用户设定的maptask个数,默认为1:min.split = InputSplit的最小值,具体可以在配置文件中修改参数maperd.mi.split.size,不配置默认为1B,block是HDFS中块的大小。
举一个例子来说:把一个258MB的文件上传到HDFS上,假设block块大小是128MB,那么它就会被分成3个block块,与之对应产生3个Split,所以最终会产生3个maptask。而第三个block块里存的文件大小只有2MB,他的block块大小是128,那么他实际占用多大空间呢?通过上述公式可知其专用的实际的文件大小,而非一个块的大小。
Map的实现逻辑和Reduce的实现逻辑都是由程序员完成的,其中map的个数和Split的个数对应起来,也就是说一个Split切片对应一个Map任务,关于Reduce的默认数是1,程序员可以自行设置。另外需要注意的是,一个程序可能只有一个map任务缺没有Reduce任务,也可能是多个MapReduce程序串接起来,比如把第一个MapReduce的输出结果作为第二个MapReduce的输如。通过阅读后面的MapReduce实例,我们会对Map和Reduce有进一步的理解。
Shuffle又叫洗牌,它起到连接Map任务与Reduce任务的作用列,在这里需要注意的是,Shuffle不是一个单独的任务,他是MapReduce执行中的步骤,如下图。
从下图可以看出,Shuffle分为两部分,一部分在Map端,一部分在Reduce端,Map处理后的数据会以 key、value的形式存在缓冲区中(buffer in memory),缓冲区大小为128MB。当该缓冲区快要溢出时(80%),会将数据写到磁盘中生成文件,也就是溢写操作(spill to disk)。溢写磁盘的过程是由一个线程来完成,溢写之前包括Partition(分区)和sort(排序),Partition和Sort都有默认实现,其中Partition分区默认是按照“hash”和“%reduce数量”进行分区的,分区之后的数据会进入不同的Reduce,而Sort是默认按照字母顺序进行排序的。我们可以根据也无需求进行编写,具体可以参考后面的实例。溢写之后会在磁盘上生成多个文件,多个文件会通过merge线程完成文件的合并,由多个小文件生成一个大文件。
合并之后的数据(以key和value的形式存在)会基于Partition被发送到不同的Reduce上,如下图中人去之间的长箭头所示,Reduce会从不同的Map上取得“属于”自己的数据并写入磁盘,完成merge操作减少文件数量,并调用Reduce程序,最终通过Output完成输出。
前面我们通过一个WordCount实例介绍列MapReduce执行过程,在这里用一个WordCount的单词统计实例来介绍如何编写MapReduce程序。
一个完整的MapReduce程序主体主要分为两个部分,一个Mapper一个Reduce
用户自定义Mapper.java类解析key/value对值,然后产生一个中间key/value对值的集合,把虽有具有相同中间key值的中间value值集合在一起后传递给Reduce函数。
用户自定义的Reduce.java类接受一个中间key的值和相关的一个value值的集合。Reduce函数合并这些value值,形成一个较小的value值的集合。每次Reduce函数调用时只产生0个或1个value输出值。通常我们通过一个迭代器把中间的value值提供给Reduce函数,这样就可以处理无法全部放入内存中的大量value值的集合。
在编写代码之前,可以先进行本机环境配置,我们也可以将程序打包后上传到centos中执行。
配置步骤如下:
文件放入hadoop解压目录下的bin文件夹里,如下图
关于第二部回头我会上传一个资源包给你们。 里面有简单的说明文档。
继承Mapper,这是一个Map过程,对输入文本进行词汇的分割并循环输出给Reducer。代码如下(下面的3个类都是我新建的)。关于导入什么JAR包,我是把hadoop/share/hadoop里面的包基本上都导入进去了。
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
public class WordCountMapper extends Mapper{
@Override
protected void map(LongWritable key,Text value,Context context)
throws IOException,InterruptedException{
//用空格进行分词
String[] str = value.toString().split(" ");
//for循环
for (int i=0;i
继承Reducer;这是一个Reduce过程,将从Map传入的词汇进行分组合并,并通过文本和单词统计量的方式输出。
import java.io.IOException;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.mapreduce.Reducer;
//mapper切分后时 a1 b1 c1 的形式,输出 a2 b1 c1形式
public class WordCountReducer extends Reducer{
//数据分组合并输出
@Override
protected void reduce (Text arg0,Iterable arg1,Context arg2)
throws IOException,InterruptedException{
int sum=0;
for(IntWritable i:arg1){
sum = sum+i.get();
}
arg2.write(arg0,new IntWritable(sum));
}
}
上面编写了Mapper和Reducer,为了使Mapper和Reducer正常运行,还需要编写主方法。主方法中我们需要先设置连接的HDFS和尧都区的文件及处理后的文件在HDFS中的路径,指明我们所要进行的Map和Reduce过程的类,然后开始MapReduce的离线数据处理
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.yarn.webapp.hamlet.Hamlet;
import org.omg.CORBA.PUBLIC_MEMBER;
import java.io.IOException;
public class Runjob {
public static void main(String[] args){
Configuration conf = new Configuration();
//namenode入口IP
conf.set("fs.defaultFS","hdfs://192.168.252.15:9000"); //大家按照自己的主机修改IP
Job job = null;
try {
//任务名字
job = Job.getInstance(conf,"mywc");
}catch (IOException e1){
e1.printStackTrace();;
}
//非主方法
job.setJarByClass(Runjob.class);
//map方法
job.setMapperClass(WordCountMapper.class);
//reducer方法
job.setReducerClass(WordCountReducer.class);
//map输出key类型
job.setOutputKeyClass(Text.class);
//map输出value类型
job.setOutputValueClass(IntWritable.class);
try{
//读取位置
FileInputFormat.addInputPath(job,new Path("/usr/input/data/wc"));
//处理完成之后的位置
FileOutputFormat.setOutputPath(job,new Path("/usr/output/data/wc"));
boolean f = job.waitForCompletion(true);
}catch (Exception e){
e.printStackTrace();
}
}
}
vim test.txt
在hdfs上创建自己的输入文件夹,就是刚才代码中的路径
hdfs dfs -mkdir -p /usr/input/data/wc #创建输入目录
hdfs dfs -put test.txt /usr/input/data/wc #将文件放入目录
输出文件不需要被创建,代码运行的时候自己会创建
Error running' xxxxxx': Command line is too long. Shorten command line for xxxxxxxxx_zcc_heu的博客-CSDN博客
class com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$Text - 墨飞_Max - 博客园
还有大家学习这个的时候,按照我这个步骤来,例如:先配置完环境变量和文件放入之后再打开IEDA。否则你颠倒过来,你得重启IDEA。
然后顺序对了,代码认真敲那就没问题的。