一、MapReduce编程模型
- MapReduce是采用一种分而治之的思想设计出来的分布式计算框架
- 如一复杂或计算量大的任务,单台服务器无法胜任时,可将此大任务切分成一个个小的任务,小任务分别在不同的服务器上并行的执行;最终再汇总每个小任务的结果
- MapReduce由两个阶段组 成:Map阶段(切分成一个个小的任务)、Reduce阶段(汇总小任务的结果)。
复制代码
map()函数的输入是kv对,输出是一系列kv对,输出写入本地磁盘。
map 阶段由一定数量的Task 组成 包括 :
1. 输入阶段格式解析:InputFormat
2. 输入数据处理:Mapper
3. 数据分组:Partitioner
reduce()函数的输入是kv对(即map的输出(kv对));输出是一系列kv对,最终写入HDFS
Reduce 阶段由一定数量的ReduceTask 组成:
1. 数据远程拷贝
2. 数据按照Key 排序
3. 数据处理Reducer
4. 数据输出格式OutputFormat
复制代码
- block对应一个分片split,一个split对应一个map task
- reduce task的个数由程序中编程指定
复制代码
MapReduce代码:
map代码
package mapraduce;
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;
public class WordMap extends Mapper<LongWritable, Text,Text, IntWritable> {
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String[] words = value.toString().split(" ");
for (String word : words) {
// 每个单词出现1次,作为中间结果输出
context.write(new Text(word), new IntWritable(1));
}
}
}
复制代码
Reduce代码:
package mapraduce;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class WordReduce extends Reducer<Text, IntWritable, Text, IntWritable> {
/*
key: hello
value: List(1, 1, ...)
*/
protected void reduce(Text key, Iterable<IntWritable> values,
Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable count : values) {
sum = sum + count.get();
}
context.write(key, new IntWritable(sum));// 输出最终结果
}
}
复制代码
整体job代码:
package mapraduce;
import java.io.IOException;
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;
public class WordMain {
public static void main(String[] args) throws IOException,
ClassNotFoundException, InterruptedException {
if (args.length != 2 || args == null) {
System.out.println("please input Path!");
System.exit(0);
}
Configuration configuration = new Configuration();
Job job = Job.getInstance(configuration, WordMain.class.getSimpleName());
// 打jar包
job.setJarByClass(WordMain.class);
// 通过job设置输入/输出格式
//job.setInputFormatClass(TextInputFormat.class);
//job.setOutputFormatClass(TextOutputFormat.class);
// 设置输入/输出路径
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 设置处理Map/Reduce阶段的类
job.setMapperClass(WordMap.class);
job.setReducerClass(WordReduce.class);
//如果map、reduce的输出的kv对类型一致,直接设置reduce的输出的kv对就行;如果不一样,需要分别设置map, reduce的输出的kv类型
// job.setMapOutputKeyClass(IntWritable.class);
// 设置最终输出key/value的类型m
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// 提交作业
job.waitForCompletion(true);
}
}
本地运行:修改mapred-site.xml中yarn改成local
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>local</value>
</property>
</configuration>
配置输入输出路径:
hdfs://node1:9000/data/customPartition.txt hdfs://node1:9000/num
复制代码
源文件:
结果:打包运行:
复制代码
WEB UI查看结果:浏览器url地址:rm节点IP:18088(例如;node1:18088)
复制代码
二: Combiner
- map端本地聚合
- 不论运行多少次Combine操作,都不会影响最终的结果
- 并非所有的mr都适合combine操作,比如求平均值
- WordCountMap与WordCountReduce代码不变
- WordCountMain中,增加job.setCombinerClass(WordCountReduce.class);
复制代码
注:
1.Combiner是MR程序中Mapper和Reduce之外的一种组件
2.Combiner组件的父类就是Reducer
3.Combiner和Reducer之间的区别在于运行的位置
4.Reducer是每一个接收全局的Map Task 所输出的结果
5.Combiner是在MapTask的节点中运行
6.每一个map都会产生大量的本地输出,Combiner的作用就是对map输出的结果先做一次合并,以较少的map和reduce节点中的数据传输量
7.Combiner的存在就是提高当前网络IO传输的性能,也是MapReduce的一种优化手段。
8.实现自定义的Combiner
1.因为源码中的Combiner是继承于Reducer,我们使用自己的Combiner就需要继承Reducer并重写reduce方法
2.job中设置job.setCombinerClass(自定义Combiner的类.class)
ps:需要注意一个点就是:Combiner就是一次Reducer类中reduce方法的实现,所以这里的KV需要和Reducer的KV是一致的
实际开发一定是先实现Mapper之后,知道了KV,然后再根据需要实现自定义的Combiner中的KV
复制代码
三、 Shuffle
参考: MapReduce的shuffle过程详解(分片、分区、合并、归并。。。)
四、自定义分区Partition
- MapReduce自带的分区器是HashPartitioner
- 原理:先对map输出的key求hash值,再模上reducetask个数,根据结果,决定此输出kv对,被匹配的reduce取走
自定义分区功能,并设定reduce个数为4
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;
import java.util.HashMap;
public class CustomPartitioner extends Partitioner<Text, IntWritable> {
public static HashMap<String, Integer> dict = new HashMap<String, Integer>();
static{
dict.put("Dear", 0);
dict.put("Bear", 1);
dict.put("River", 2);
dict.put("Car", 3);
}
public int getPartition(Text text, IntWritable intWritable, int i) {
//
int partitionIndex = dict.get(text.toString());
return partitionIndex;
}
}
复制代码
五、二次排序