先分再合,分而治之
分解为若干个“简单的子任务”来进行并行处理
,可以进行拆分的前提是这些小人物可以并行运算,彼此之间几乎没有依赖
不可拆分的计算任务或相互依赖的数据无法进行并行运算
MapReduce最大的亮点在于通过抽象模型和计算框架把需要做什么与具体怎么做分开了,为程序员提供一个抽象和高层的编程接口框架
程序员需要关心其应用层的具体计算问题,仅需编写少量的处理应用本身计算问题的业务程序代码
诸多系统层细节全部都被隐藏起来
,交给计算框架去处理;从分布式代码的执行到大到数千小到单个节点集群的自动调度使用Hadoop MapReduce是一个分布式计算框架,用于轻松编写分布式应用程序,这些应用程序以可靠、容错的方式并行处理大型硬件集群(数千个节点)上的大量数据(多TB数据集)
MapReduce是一种面向数据处理的一种指导思想,也是用于对大规模数据进行分布式计算的编程模型
它的出现解决了人们在面零海量数据束手无策的问题,同时他还是易于使用和高拓展的,使得开发者无需关心分布式系统底层的复杂性即可很容易地编写分布式数据处理程序,并在呈现上万台普通服务器中运行
MapReduce框架提供了用于二次开发的接口:简单的实现一些接口就可以完成一个分布式程序。任务计算交给计算框架去处理,将分布式程序部署到Hadoop集群上运行和,集群节点可以拓展到成千上百个
当计算机资源不能得到满足的时候,可以通过增加记起来拓展他的计算能力,基于MapReduce的分布式计算的特点可以随机额点数目的增加保持近似于线性的增长,这个特点是MapReduce处理海量数据的关键,通过将计算节点增加至几百或者几千可以很容易处理数百TB甚至PB级别的离线数据
Hadoop集群是分布式搭建和部署的,任何单一机器节点宕机了,它可以把上面的计算机任务转移到令一节点上运行,不影响整个作业的完成,过程完全是由Hadoop内部完成的
可以处理GB、TB和PB级别的数据量
MapReduce有很多优势,也有相对的局限性,局限性不代表不能做,而是在某些场合下实现的效果比较差,并不适合MapReduce来进行处理。
MapReduce主要应用于离线作业,无法做到秒级或者亚秒级的数据相应
流式计算特点是数据时源源不断地计算的,并且数据是同台的。而MapReduce是一个离线计算框架,主要是针对静态数据集得到的,数据是不能动态变化的。
一个完整的mapReduce程序在分布式运行时有三种实时进程
MRAppMaster
:负责整个程旭过程调度及状态协调MapTask
:负责Map阶段的整个数据处理流程ReduceTask
:负责Reduce阶段的整个数据处理流程MapReduce编程模型只能包含一个Map阶段和一个Reduce阶段,如果用户的业务非常复杂,那就只能多个MapReduce程序穿行运行
虽然MapReduce从外表上看来就两个阶段Map和Reduce,但是内部却包含了许多默认组件和默认行为。包括:
组件:读取数据左键InputFormat、输出数据组件OutputFormat
行为:排序(key的字典需排序)、分组(Reduce阶段key相同的分为一组、一组调用一次reduce处理)
序列化:序列化是将结构化对象转换成字节流便于进行网络传输或者写入持久化存储的过程
反序列化: 反序列化是将字节流转范围一些列结构化对象的过程,重新创建该对象
Java对象序列化的机制,把对象表示成一个二进制字节数组
,里面包含了对象的数据、对象的数据类型、对象内部的数据的类型信息,通过保存或者转移这些二进制数据达到持久化、传输的目的java.io.Serializable
接口,反序列化时和序列化相反的过程,就是将二进制数组转化为对象的过程Hadoop数据类型 | Java数据类型 |
---|---|
BooleanWritable | boolean |
ByteWritable | byte |
IntWritable | int |
FloatWritable | float |
LongWritable | long |
DoubleWritable | double |
Text | String |
MapWritable | map |
ArrayWritable | array |
NullWritable | null |
在这里将进行一个案例分析: WordCount:字词统计,这里将对其词语个数进行一个分析,其本质比较简单,说到底就是根据分隔符进行一个拆分,随后进行统计,整体流程比较简单,主要目的是通过此次案例分析,更好的认识、理解MapReduce。
<dependencies>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-commonartifactId>
<version>3.1.4version>
dependency>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-clientartifactId>
<version>3.1.4version>
dependency>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-hdfsartifactId>
<version>3.1.4version>
dependency>
<dependency>
<groupId>org.apache.hadoopgroupId>
<artifactId>hadoop-mapreduce-client-coreartifactId>
<version>3.1.4version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
dependency>
dependencies>
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
/**
* @author wxk
*/
public class WordMapper extends Mapper<LongWritable, Text,Text,LongWritable> {
private Text keyOut =new Text();
private final LongWritable out=new LongWritable(1);
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String [] worlds = value.toString().split("\\s+");
for (String word : worlds){
keyOut.set(word);
//写入到上下文中
context.write(keyOut,out);
}
}
}
在这里我将所有的包也写入了进去,主要目的是防止导包倒错。比如说Text是hadoop.io.Text
而不是其他的Text
再导入包之后对其Mapper进行一个解释:
在这里可以看到这里存在4个值 KEYIN
VALUEIN
KEYOUT
VALUEOUT
分别对应进入的键、进入的值,输出出去的键、输出出去的值。
根据我们的业务,输入的键是偏移量,值是字符串,而输出的时候键是字符串,值是数字。兼顾大文本量,这里采用Long
所以到最后我们的继承就是Mapper
那么后续的Reduce理解起来就相对比较容易了
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/**
* @author wxk
*/
public class WordReduce extends Reducer<Text, LongWritable,Text,LongWritable> {
private LongWritable result=new LongWritable();
@Override
protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
long count = 0;
for (LongWritable value : values){
count+=value.get();
}
result.set(count);
//输出
context.write(key,result);
}
}
V1.0版本
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
/**
* @author wxk
*/
public class WordDriver {
public static void main(String[] args) throws Exception {
//配置文件对象
Configuration conf = new Configuration();
// 创建作业实例
Job job = Job.getInstance(conf,WordDriver.class.getSimpleName());
//设置作业的驱动
job.setJarByClass(WordDriver.class);
//设置Mapper
job.setMapperClass(WordMapper.class);
//设置Reduce
job.setReducerClass(WordReduce.class);
//设置Mapper阶段输出的Key
job.setMapOutputKeyClass(Text.class);
//设置Mapper阶段输出的Value
job.setMapOutputValueClass(LongWritable.class);
//设置Reduce阶段输出的Key的类型
job.setOutputKeyClass(Text.class);
//设置Reduce阶段输出的Value的类型
job.setOutputValueClass(LongWritable.class);
//设置文件的路径 《上级文件夹》
FileInputFormat.addInputPath(job,new Path(args[0]));
//设置输出结果的文件夹
FileOutputFormat.setOutputPath(job,new Path(args[1]));
//提交任务
boolean b = job.waitForCompletion(true);
//判断任务是否执行成功
if (b){
System.out.println("OK");
}else{
System.out.println("error");
}
}
}
V2.0版本:MapReduce推荐使用该版本进行操作
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
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.util.Tool;
import org.apache.hadoop.util.ToolRunner;
/**
* @author wxk
* @date 2022/11/21/8:53
*/
public class WordCount2 extends Configured implements Tool {
public static void main(String[] args) throws Exception {
Configuration configuration =new Configuration();
int run = ToolRunner.run(configuration, new WordCount2(), args);
System.out.println("status="+run);
}
@Override
public int run(String[] args) throws Exception {
// 创建作业实例
Job job = Job.getInstance(getConf(),WordCount2.class.getSimpleName());
//设置作业的驱动
job.setJarByClass(WordDriver.class);
//设置Mapper
job.setMapperClass(WordMapper.class);
//设置Reduce
job.setReducerClass(WordReduce.class);
//设置Mapper阶段输出的Key
job.setMapOutputKeyClass(Text.class);
//设置Mapper阶段输出的Value
job.setMapOutputValueClass(LongWritable.class);
//设置Reduce阶段输出的Key的类型
job.setOutputKeyClass(Text.class);
//设置Reduce阶段输出的Value的类型
job.setOutputValueClass(LongWritable.class);
//设置输入文件的文件夹路径 切记:一定是文件夹路径而不是文件的具体路径
FileInputFormat.addInputPath(job,new Path(args[0]));
//设置输出结果的文件夹
FileOutputFormat.setOutputPath(job,new Path(args[1]));
return job.waitForCompletion(true)?0:1;
}
}
在pom.xml中添加以下代码:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-jar-pluginartifactId>
<version>2.4version>
<configuration>
<archive>
<manifest>
<addClasspath>trueaddClasspath>
<classpathPrefix>lib/classpathPrefix>
<mainClass>MapReduceTest.WordDrivermainClass>
manifest>
archive>
configuration>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
在这里指定入口:随后点击Maven的Clean 然后点击package即可,随后将target下的文件进行复制到服务器上
在以往的搭建中,运行Jar包是通过Jar这个命令,但是MapReduce这个文件的Jar包他好需要依赖于Hadoop,比如说HDFS等等,这里Hadoop就提供了相应的方法,可以直接完成:
hadoop jar ***.jar args1 args2
这里的args1 、args2是因为方法中要使用args参数,所以需要传参,如果在文件中已经写死了,那么就没有必要在写了
运行jar包,运行结果:
去查看文件输出
可见分词成功
进行逻辑切片
,形成切片规划默认是按照行读取数据,Key是每一行的起始位置偏移量,Value是本行的文本内容
每读取解析出来一个,就调用一次Map方法
溢出spil
的时候根据key进行排序sortmerge合并
,成为一个文件。执行流程图
复制拉起
其输出的键值对合并merge
,即把分散的数据合并成一个大的数据,在对合并后的数据进行排序
调用reduce方法
键相等的键值对调用一次reduce方法,最后把这些输出的键值对写入到HDFS文件中