1、什么是mapreduce
MapReduce是Hadoop核心三剑客之一,设计思想来源于谷歌三篇论文之一的《分布式计算模型》。作为一个分布式运算程序编程框架,需要用户实现业务逻辑代码并和它自带的默认组件整合成完整的分布式运算程序,并发运行在Hadoop集群上。一个完整的MapReduce程序在分布式运行时有三类实例进程:
1. MRAppMaster:负责整个程序过程调度及状态协调
2. MapTask:负责map阶段整个数据处理流程
3. ReduceTask:负责reduce阶段整个数据处理流程
2、MapReduce的处理流程:以word Count为例子
package wordcount;
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;
/**
* MapReduce wordCount 单词计数
*/
public class WcDrive {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//1 获取一个Job实例
Job job = Job.getInstance(new Configuration());
//2 设置我们的类路径
job.setJarByClass(WcDrive.class);
//3 设置Mapper 和 Reducer
job.setMapperClass(WcMapper.class);
job.setReducerClass(WcReduce.class);
//4 设置Mapper 和 Reducer 输出的类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//5 设置输入输出数据源
// FileInputFormat.setInputPaths(job,new Path("C:\\Users\\52683\\Desktop\\JavaPartDemo\\hdfsLearn\\src\\main\\ExampleFile\\wordcount.txt"));
// FileOutputFormat.setOutputPath(job,new Path("C:\\Users\\52683\\Desktop\\JavaPartDemo\\hdfsLearn\\src\\main\\ExampleFile\\output"));
FileInputFormat.setInputPaths(job,new Path(args[0]));// /home/data/xxx.txt
FileOutputFormat.setOutputPath(job,new Path(args[1]));//
//6 提交我们的job
boolean b = job.waitForCompletion(true);
System.out.println(b ? 0 : 1);
}
}
package wordcount;
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 WcMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
private Text word = new Text();
private IntWritable one = new IntWritable(1);
/**
* @param key map阶段输入key的类型
* @param value map阶段输入value的类型
* @param context 上下文环境
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
/**
* 这里的执行过程是一行一行的读取,读取一行就会write一次
* map输出的形式是:
* 下面是我们的输入的文件
* a a
* e f
* g
* 那么我们map阶段的输出是:
* (a,1)
* (a,1)
* (e,1)
* (f,1)
*/
//拿到这一行数据
String line = value.toString();
//按照空格进行切分
String[] words = line.split(" ");
for (String word : words) {
this.word.set(word);
context.write(this.word,this.one);
}
}
}
package wordcount;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class WcReduce extends Reducer<Text, IntWritable,Text,IntWritable> {
private IntWritable total = new IntWritable();
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
/**
* reduce 阶段的输入是map阶段的输出
* reduce 接收到的输入数据的格式是:
* a,(1,1)
* e,(1)
* f,(1)
*/
//做累加
int sum = 0;
for (IntWritable value : values) {
sum += value.get();
}
//包装结果并输出
total.set(sum);
context.write(key,total);
}
}
下面我们以一张图来看一下整体的流程:
上面我们抛砖引玉,以wordCount的例子来梳理一下我们流程。那么下面我们对里面的细节进行刨析。
首先是Map阶段,文件的加载切分,这里切分我们来细究一下如何切分。
在我们wordCount 的例子中我们并没有找到我们切分相关的代码,这是什么原因呢,是因为我们的MapReduce如果不指定切分的形式默认的切分形式是:TextInputFormat。
那么说到这里,我们就记录一下MapReduce 的 inputFormat:
InputFormat主要作用:
对输入的文件进行切分,形成多个InputSplit文件,每一个InputSplit对应着一个map任务
创建RecordReader,从InputSplit分片中读取数据供map使用
二 有几个比较重要的实现
FileInputFormat: 主要用于处理文件的一个InputFormat类,它包括子类:
FiexedLengthInputFormat: 读取输入文件的固定长度的记录,这种文件不该是文本文件,二进制文件比较多
KeyValueInputFormat: 用于读取普通文本文件,文件按照行分割,每一行由key和value组成,key 和 value的分隔符若没有指定,那么整行为key,value为空
TextInputFormat: 文件按照行划分,key就是这一行在文件中的偏移量,value就是这一行文本
SequenceFileInputFormat:
NLineInputFormat: 是可以将N行数据划分为一个Split,作为MapTask输入
DBInputFormat: 主要用于处理数据库数据的InputFormat类
CombineFileInputFormat:我们知道文件切分的时候,FileInputFormat默认情况下,如果一个文件小于blockSize,那么这个文件就是一个InputSplits, 如果大于blockSize,则是先按照文件大小/blockSize,如果有剩余单独成为一个InputSplit。