MapReduce介绍
MapReduce的思想核心是分而治之,适用于大量复杂的任务处理场景(大规模数据处理场景)
map负责分,把负责的任务分解为若干个简单的任务来并行处理,可以进行拆分的前提是这些小任务可以并行计算,彼此之间几乎没有依赖关心。
reduce负责合,对map阶段的结果进行全局汇总
需求:a.txt 300M统计文件中每个单词出现的次数
在实际应用中,不一定只有一个reduce,不同的reduce汇总不同类型的数据。
MapReduce设计构思
MapReduce是一个分布式运算程序的编程框架,核心功能是将用户编写的业务逻辑代码和自带默认组件整合一个完整的分布式运算程序,并发运行在hadoop集群上。
MapReduce中定义了map和reduce两个抽象的编程接口,由用户去编程实现map和reduce,MapReduce处理的数据类型是<key,value>键值对。
一个完整的MapReduce程序在分布式运行时有三类实例进程:
MRAppMaster 负责整个程序的过程调度及状态协调
maptask负责map阶段的整个数据处理流程
reduceTask 负责reduce阶段的整个数据处理流程
mapreduce编程流程
map阶段2个步骤
1、设置inputFormat类,将数据切分为key-value(k1和v1)对,输入到第二步
2、自定义map逻辑,将第一步的结果转换成另外的key-value(k2和v2)对,输出结果
shuffle阶段4个步骤
1、对输出的key-value对进行分区
2、对不同分区的数据按照相同的key排序
3、(可选)对分组的数据初步规约,降低数据的网络拷贝
4、对数据进行分组,相同key的value放入一个集合
reduce阶段的2个步骤
1、对多个map任务的结果进行排序及合并,编写reduce函数实现自己的逻辑,对输入的key-value(新k2、v2)进行处理,转为新的key-value(k3和v3)输出
2、设置OutputForamt处理并保存reduce输出的key-value数据
程序
1、导入依赖
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-auth</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、map阶段的代码
将k1 v1 转为k2 v2 并且写入到context上下文中
package cn.kgc.kb11.wc;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
/*
四个泛型
KEYIN:K1的类型
VALUEIN: V1的类型
KEYOUT: K2的类型
VALUEOUT:V2的类型 为了解决序列化的问题,自己定义了类型
LongWritable-java long Text-String IntWritable-int
*/
public class WCMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
@Override
//重写map方法 将k1 v1 转为k2 v2
/*
参数:key :k1 行偏移量
value:v1 每行的文本数据
context:表示上下文对象
*/
/*
如何将k1 v1 转为k2 v2
k1 v1
0 hello,world,hadoop
11 hdfs,hive,hello
k2 v2
hello 1
world 1
hadoop 1
hdfs 1
*/
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//将文本数据进行拆分
//遍历数组 组装k2 v2
//将k2 v2 写入上下文中
Text text = new Text();
IntWritable intWritable = new IntWritable();
String str = value.toString();
String[] words = str.split("[ |,|']");
for (String word : words) {
//参数是Text IntWritable对象
text.set(word);
intWritable.set(1);
context.write(text,intWritable);
//context.write(new Text(word),new IntWritable(1));
}
}
}
3、reduce阶段
package cn.kgc.kb11.wc;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/*
四个泛型
KEYIN:K2的类型
VALUEIN: V2的类型
KEYOUT: K3的类型
VALUEOUT:V3的类型 为了解决序列化的问题,自己定义了类型
LongWritable-java long Text-String IntWritable-int
*/
public class WCReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
//将新的k2 v2 转为 k3 v3
//将k3 v3 写到上下文中
// key:新k2 values:新v2是一个集合 context上下文对象
//如何将新k2 v2 转为k3 v3
//新 k2 v2
// hello <1,1,1>
// k3 v3
// hello 3 遍历集合数字相加得到v3 将k3 v3写到上下文中
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int sum=0;
for (IntWritable value : values) {
sum+=value.get();
}
context.write(key,new IntWritable(sum));
}
}
4、定义主类
package cn.kgc.kb11.wc;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import java.io.IOException;
public class WCDriver {
public static void main(String[] args) {
Configuration conf=new Configuration();
Job job=null;
try {
//1、创建job任务对象
job=Job.getInstance(conf,"wc");
} catch (IOException e) {
e.printStackTrace();
}
//使用本类作为mapreduce的驱动(关联jar包)
job.setJarByClass(WCDriver.class);
//设置对应的mapper类和reducer类
job.setMapperClass(WCMapper.class);
job.setReducerClass(WCReducer.class);
//设置mapper类输出的kv类型
job.setMapOutputValueClass(IntWritable.class);
job.setMapOutputKeyClass(Text.class);
//设置最终输出的KV类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//设置输入和输出路径
try {
FileInputFormat.setInputPaths(job,new Path("D:/idea_workSpace/testMR/a.txt"));
} catch (IOException e) {
e.printStackTrace();
}
FileOutputFormat.setOutputPath(job,new Path("D:/idea_workSpace/testMR/out1"));
boolean isSuccess=false;
try {
//提交job
isSuccess=job.waitForCompletion(true);//是否监控和打印job
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(isSuccess?"运行成功":"运行失败");
}
}
在hadoop上运行
package cn.kgc.kb11.wc1;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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 java.io.IOException;
public class WCDriver {
public static void main(String[] args) {
Configuration conf=new Configuration();
Job job=null;
try {
//1、创建job任务对象
job=Job.getInstance(conf,"wc");
} catch (IOException e) {
e.printStackTrace();
}
//使用本类作为mapreduce的驱动(关联jar包)
job.setJarByClass(WCDriver.class);
//设置对应的mapper类和reducer类
job.setMapperClass(WCMapper.class);
job.setReducerClass(WCReducer.class);
//设置mapper类输出的kv类型
job.setMapOutputValueClass(IntWritable.class);
job.setMapOutputKeyClass(Text.class);
//设置最终输出的KV类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//设置输入和输出路径
try {
FileInputFormat.setInputPaths(job,new Path(args[0]));
} catch (IOException e) {
e.printStackTrace();
}
FileOutputFormat.setOutputPath(job,new Path(args[1]));
boolean isSuccess=false;
try {
//提交job
isSuccess=job.waitForCompletion(true);//是否监控和打印job
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(isSuccess?"运行成功":"运行失败");
}
}
1打包
2、将文件a.txt上传到hadoop /test
3、运行
hadoop jar /root/testMR.jar cn.kgc.kb11.wc1.WCDriver /test/a.txt /output
mapreduce执行过程
mapreduce的过程属于yarn的运行过程
client->分片(按block)->split(按行,形成<k:v>)->map->环形缓冲区->shuffle->reduce->hdfs->client
分片:按照块进行分片,一块一个分片,当后一个块大小小于10%,会合并到前一块,一个Datanode上可能会有多个分片
为什么Mapper输入是<LongWritable,Text>?
split(<k,v>) 后形成 <key,value> —><偏移量,文本内容>
为什么reducer在不同机器上执行,最后结果都在一个文件夹下?
因为 1、数据备份(SecondaryNamenode完成数据备份后)2、程序运行完之后是由app Master->app Manager统一上传至hdfs