MapReduce优点:海量数量离线处理、易开发、易运行
MapReduce缺点:实时流式计算
wordcount入门:统计文件中每个单词出现的次数
需求:求wc
- 文件内容小:shell
- 文件内容很大:TB GB ??? 如何解决大数据量的统计分析
借助于分布式计算框架:MapReduce
分而治之
(input) <k1, v1> -> map -> <k2, v2> -> combine -> <k2, v2> -> reduce -> <k3, v3> (output)
核心概念
Split:交由MapReduce作业来处理的数据块,是MapReduce中最小的计算单元
HDFS:blocksize 是HDFS中最小的存储单元 128M
默认情况下:他们两是一一对应的,当然我们也可以手工设置他们之间的关系(不建议)
InputForamt:将我们的输入数据进行分片(split):InputSplit[] getSplits(JobConf var1, int var2) throws IOException;
TextInputForamt: 处理文本格式的数据
OnputForamt:输出
Combiner:本地的reducer、减少Map Tasks输出的数据量及数据网络传输量
Partitioner:Partitioner决定Map Tasks输出的数据交由哪个ReduceTask处理、默认实现:分发的key的hash值对ReduceTask个数取模
MapReduce1.x的架构
- JobTracker:JT
- 作业的管理者 管理的
- 将作业分解成一堆的任务:Task(MapTask和ReduceTask)
- 将任务分派给JobTracker运行
- 作业的监控、容错处理(task作业挂了,重启task的机制)
- 在一定的时间间隔内,JT没有收到TT的心跳信息,TT可能是挂了,TT上运行的任务会被指派到其他TT上去执行
- TaskTracker:TT
- 任务的执行者 干活的
- 在TT上执行我们的Task(MapTask和ReduceTask)
- 会与JT进行交互:执行、启动、停止作业,发送心跳信息给JT
- MapTask
- 自己开发的map任务交由该Task出来
- 解析每条记录的数据,交给自己的map方法处理
- 将map的输出结果写到本地磁盘(有些作业只仅有map没有reduce==》HDFS)
- ReduceTask
- 将Map Task输出的数据进行读取
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
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.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;
/**
* 使用mapreduce开发wordcount应用程序
* @author: lyj
* @since: 2019/4/3
*/
public class CombinerWordCountApp {
/**
* Map:读取输入的文件
*/
public static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable> {
LongWritable one = new LongWritable(1);
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//接收到的每一行数据
String line = value.toString();
//按照指定分割符进行拆分
String[] words = line.split(" ");
for (String word : words) {
//通过上下文把map的处理结果输出
context.write(new Text(word), one);
}
}
}
/**
* Reduce:归并操作
*/
public static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable> {
@Override
protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
long sum = 0;
for (LongWritable value : values) {
//求key出现的次数总和
sum += value.get();
}
//最终统计结果的输出
context.write(key, new LongWritable(sum));
}
}
/**
* 定义Driver:封装了MapReduce作业的所有信息
*
* @param args
*/
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//定义Configuration
Configuration configuration = new Configuration();
//准备清理已存在的输出目录
Path outputPath = new Path(args[1]);
FileSystem fileSystem = FileSystem.get(configuration);
if (fileSystem.exists(outputPath)){
fileSystem.delete(outputPath,true);
System.out.println("output file exists, but is has deleted");
}
//创建job
Job job = Job.getInstance(configuration,"wordcount");
//设置job的处理类
job.setJarByClass(CombinerWordCountApp.class);
//设置作业处理的输入路径
FileInputFormat.setInputPaths(job,new Path(args[0]));
//设置map相关的参数
job.setMapperClass(MyMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
//设置reduce相关的参数
job.setReducerClass(MyReducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
//通过job设置combiner处理类,其实逻辑上和reducer是一模一样的
job.setCombinerClass(MyReducer.class);
//设置作业处理的输出路径
FileOutputFormat.setOutputPath(job,new Path(args[1]));
System.exit(job.waitForCompletion(true)?0:1);
}
}
在MR中,输出文件是不能事先存在的
- 先手工通过shell的方式将输出文件夹先删除:hadoop fs -rm -r /output/wc
- 在代码中完成自动删除功能:推荐大家使用这种方式
-
//准备清理已存在的输出目录 Path outputPath = new Path(args[1]); FileSystem fileSystem = FileSystem.get(configuration); if (fileSystem.exists(outputPath)){ fileSystem.delete(outputPath,true); System.out.println("output file exists, but is has deleted"); }
-