MapReduce入门-模型介绍,工作机制,单词计数(wordCount)及代码详解

1.简单了解MapReduce

MapReduce是hadoop的核心组件之一,hadoop要分布式包括两部分,一是分布式文件系统hdfs,二是分布式计算框,就是mapreduce,缺一不可,也就是说,可以通过mapreduce很容易在hadoop平台上进行分布式的计算编程 。

MapReduce最早是由Google公司研究提出的一种面向大规模数据处理的并行计算模型和方法。Google公司设计MapReduce的初衷主要是为了解决其搜索引擎中大规模网页数据的并行化处理。2003年和2004年,Google公司在国际会议上分别发表了两篇关于Google分布式文件系统和MapReduce的论文,公布了Google的GFS和MapReduce的基本原理和主要设计思想

下面我给出一个mapreduce与之前传统并行计算框架的比较:

 传统并行计算架构MapReduce
集群架构/容错性共享式(共享内存/存储),容错性差非共享式,容错性好
硬件/价格/扩展性刀片服务器,价格高,扩展性差普通pc即可,扩展性强
适用场景实时,细粒度计算,计算密集型批处理,非实时,数据密集型

2.MapReduce模型介绍

1.MApReduce将并行计算过程高度抽象到2个最基本的函数:map()和reduce()

2.采用“分而治之”策略,大规模的数据集合会被切分为许多独立的分片(split),这些分片可以被多个map任务并行处理(Hadoop为每个split分配一个map任务,理想分片大小为一个HDFS块)

Mapper负责“分”,即把复杂的任务分解为若干个“简单的任务”来处理。

“简单的任务”包含三层含义: 一是数据或计算的规模相对原任务要大大缩小;二是就近计算原则,即任务会分配到存放着所需数据的节点上进行计算;三是这些小任务可以并行计算,彼此间几乎没有依赖关系。

Reducer负责对map阶段的结果进行汇总。至于需要多少个Reducer,用户可以根据具体问题,通过在mapred-site.xml配置文件里设置参数mapred.reduce.tasks的值,缺省值为1

3.MapReduce框架采用Master/Slave架构,包括一个Master和若干个Slave。Master上运行JobTracker,Slave上运行TaskTracker

3.MapReduce的工作机制

之前的博文中我提到过Hadoop到现在有1.x 2.x的版本,相对应的 作为Hadoop原生的分布式并行计算框架,MapReduce也会有相对应的1.x 2.x版本。

首先,简要介绍MapReduce1.x。

虽然现在MapReduce1.x的版本几乎已经不在实际中应用了,这里我还是简单做个小结。

MapReduce1.x的架构:

MapReduce的整个工作过程如上图所示,它包含如下4个独立的实体:   

实体一:客户端,用来提交MapReduce作业。   

实体二:JobTracker,用来协调作业的运行。  

作业的管理者

将作业分解成一堆的任务:Task (MapTask和ReduceTask)

将任务分派给TAskTracker运行

作业的监控、容错处理(task作业挂了,重启task的机制)

在一定的时间间隔内,JT没有收到TT的心跳信息,TT可能是挂了,TT上运行的任务会被指派到其他TT上去执行 

实体三:TaskTracker,用来处理作业划分后的任务。   

任务的执行者

在TT上执行我们的Task(MapTask和ReduceTask)

会与JT进行交互:执行/启动/停止作业,发送心跳信息给JT

MapTask:自己开放开发的map任务交由改Task出来

解析每天记录的数据,交给自己的map方法处理

将map的输出结果写到本地磁盘(有些作业仅有map没有reduce==>HDFS)

ReduceTask:将MapTask输出的数据进行读取

按照数据进行分组传给我们自己编写的reduce方法处理

输出结果写到HDFS

实体四:HDFS,用来在其它实体间共享作业文件。 

HDFS这一部分,更多内容详见我的上一篇博文HDFS笔记

 

接下来 介绍MapReduce2.x

这里 我们这样理解,ResourceManager(RM)等同于JT,NodeManager(NM)等同于TT。

补充2.x里面的新内容:

MapReduce编程之Combiner:

作用相当于本地的reducer

减少Map Tasks输出的数据量级数据网络传输量

Combiner使用场景:求和  次数(可以)

平均数(不行)

 

MapReduce编程之Partitioner:

Partitioner决定MapTask输出的数据交由那个ReducerTask处理

默认实现:分发的key的hash值对Reduce Task个数取模

4.MapReduce计算框架

讲述MapReduce计算框架,我们通过下面这张图讲解。

简单来说,MapREduce执行的全过程包括一下几个阶段:从HDFS中读入数据,执行map任务输出中间结果,通过shuffle阶段吧中间结果分区排序整理后发送给reduce任务,执行reduce任务得到最终结果并写入HDFS中。在执行的全过程中,shuffle阶段最为重要和关键。

5. 数据流

(2020.1.9更新)

这里有一点注意: reduce任务的数量并非输入数据的大小决定的,他是可以独立指定的

这里提到的图2-4 是MapReduce程序中最常用的模型

我们在后面的博客中会提到一个MapJoin和ReduceJoin,而MapJoin采用的就是图2-5的形式,他在Hive中比较常见。

6.MapReduce编程-wordCount

接下来就mapreduce的执行过程,编写一个入门的小程序单词计数。希望通过一下代码 读者体会mapreduce的计算框架

package MapReduce;

import java.io.IOException;
import java.net.URI;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
/**
 * hadoop入门第一个小程序  利用MApReduce多文件中的单词计数
 * @author xjh 2018.07.28
 *
 */
public class WordCount {
	
	public static final String HDFS_PATH="hdfs://msiPC:9000";
	//该类中包含两个内部类
	/**
	 *Mapper类 	 
         * map方法完成工作就是读取文件
         * 将文件中每个单词作为key键,值设置为1,
         * 然后将此键值对设置为map的输出,即reduce的输入
         *
	 */
	public static class WordCountMap extends
			Mapper<LongWritable, Text, Text, IntWritable> {
		private final IntWritable one = new IntWritable(1);
		private Text word = new Text();
		
		public void map(LongWritable key, Text value, Context context)
				throws IOException, InterruptedException {
			String line = value.toString();	//接受到的每一行数据
			StringTokenizer token = new StringTokenizer(line);
			while (token.hasMoreTokens()) {
				word.set(token.nextToken());
				context.write(word, one);	//将单词作为key(value=1)写入context中
			}
		}
		/**
         * StringTokenizer:字符串分隔解析类型
         * 之前没有发现竟然有这么好用的工具类
         * java.util.StringTokenizer
         * 1. StringTokenizer(String str) :
         *  构造一个用来解析str的StringTokenizer对象。
         *  java默认的分隔符是“空格”、“制表符(‘\t’)”、“换行符(‘\n’)”、“回车符(‘\r’)”。
         * 2. StringTokenizer(String str, String delim) :
         *  构造一个用来解析str的StringTokenizer对象,并提供一个指定的分隔符。
         * 3. StringTokenizer(String str, String delim, boolean returnDelims) :
         *  构造一个用来解析str的StringTokenizer对象,并提供一个指定的分隔符,同时,指定是否返回分隔符。
         */
	}
	
	 /**
     * reduce的输入即是map的输出,将相同键的单词的值进行统计累加
     * 即可得出单词的统计个数,最后把单词作为键,单词的个数作为值,
     * 输出到设置的输出文件中保存
     */
	public static class WordCountReduce extends
			Reducer<Text, IntWritable, Text, IntWritable> {
		public void reduce(Text key, Iterable<IntWritable> values,
				Context context) throws IOException, InterruptedException {
			int sum = 0;
			for (IntWritable val : values) {
				sum += val.get();	//相同单词进行累加
			}
			context.write(key, new IntWritable(sum));
		}
	}

	public static void main(String[] args) throws Exception {
		//main()函数首先进行Hadoop的相关配置类
		Configuration conf = new Configuration();	//创建COnfiguration
		
		//因为HDFS默认情况下是输出文件路径事先不存在 所以 我们在这里新增一个对 已存在输出文件清理的功能
		//除了在代码中实现,我们还可以在hdfs 命令行手动删除(比较low的方法)
		Path outputPath=new Path(args[1]);
		FileSystem file = FileSystem.get(new URI(HDFS_PATH),conf);		//这里需要加上hdfs的URI地址
		if(file.exists(outputPath)){
			//如果存在,则删除
			file.delete(outputPath,true);
			System.out.println("output path exists,but it's deleted...");
		}
		
		Job job = new Job(conf);					//创建job
		job.setJarByClass(WordCount.class);			//设置job的处理类
		job.setJobName("wordcount");				//job名称
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);
		job.setMapperClass(WordCountMap.class);		//配置Map方法的类
		job.setReducerClass(WordCountReduce.class);	//配置Reduce方法的类
		job.setInputFormatClass(TextInputFormat.class);
		job.setOutputFormatClass(TextOutputFormat.class);
		FileInputFormat.addInputPath(job, new Path(args[0]));	//作业处理输入路径
		FileOutputFormat.setOutputPath(job, new Path(args[1]));	//作业处理输出路径
		job.waitForCompletion(true);		//作业提交
	}
}

这个Mapper类是一个泛型类,他有四个形参类型,分别指定mao函数的输入键,输入值,输出键,输出值。这里的LongWritable类型可理解为Java中的Long型,Text型可理解为Java中的String类型,IntWritable类型可理解为Java的Integer类型。

map()方法的输入 是一个键和一个值,程序中我们首先将包含有一行输入的text文本转换成String形式,然后进行分词和计数。map方法提供Context实例作为输入内容的写入。

相对应的Reducer类也有四个形式参数类型用于指定输入和输出类型。由上述的理论知识,我们知道Reducer类的输入类型必须匹配mao函数的输出类型:即Text类型和IntWritable类型, 而且你可以看到Reducer类的输出类型同样是Text类型和IntWritable类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值