MapReduce介绍

MapReduce介绍

(一)MapReduce简介
MapReduce是一种分布式计算模型,是Google提出的,主要用于搜索领域,解决海量数据的计算问题。
MR有两个阶段组成:Map和Reduce,用户只需实现map()和reduce()两个函数,即可实现分布式计算。
(二)MapReduce有哪些角色?各自的作用是什么?
MapReduce由JobTracker和TaskTracker组成。
JobTracker负责资源管理和作业控制,TaskTracker负责任务的运行。
(三)MapReduce程序执行流程
程序执行流程图如下:
在这里插入图片描述
(1) 开发人员编写好MapReduce program,将程序打包运行。
(2) JobClient向JobTracker申请可用Job,JobTracker返回JobClient一个可用Job ID。
(3) JobClient得到Job ID后,将运行Job所需要的资源拷贝到共享文件系统HDFS中。
(4) 资源准备完备后,JobClient向JobTracker提交Job。
(5) JobTracker收到提交的Job后,初始化Job。
(6) 初始化完成后,JobTracker从HDFS中获取输入splits(作业可以该启动多少Mapper任务)。
(7) 与此同时,TaskTracker不断地向JobTracker汇报心跳信息,并且返回要执行的任务。
(8) TaskTracker得到JobTracker分配(尽量满足数据本地化)的任务后,向HDFS获取Job资源(若数据是本地的,不需拷贝数据)。
(9) 获取资源后,TaskTracker会开启JVM子进程运行任务。
注:
(3)中资源具体指什么?主要包含:
● 程序jar包、作业配置文件xml
● 输入划分信息,决定作业该启动多少个map任务
● 本地文件,包含依赖的第三方jar包(-libjars)、依赖的归档文件(-archives)和普通文件(-files),如果已经上传,则不需上传
(四)MapReduce工作原理
工作原理图如下:
在这里插入图片描述
map task
程序会根据InputFormat将输入文件分割成splits,每个split会作为一个map task的输入,每个map task会有一个内存缓冲区,输入数据经过map阶段处理后的中间结果会写入内存缓冲区,并且决定数据写入到哪个partitioner,当写入的数据到达内存缓冲区的的阀值(默认是0.8),会启动一个线程将内存中的数据溢写入磁盘,同时不影响map中间结果继续写入缓冲区。在溢写过程中,MapReduce框架会对key进行排序,如果中间结果比较大,会形成多个溢写文件,最后的缓冲区数据也会全部溢写入磁盘形成一个溢写文件(最少有一个溢写文件),如果是多个溢写文件,则最后合并所有的溢写文件为一个文件。

reduce task
当所有的map task完成后,每个map task会形成一个最终文件,并且该文件按区划分。reduce任务启动之前,一个map task完成后,就会启动线程来拉取map结果数据到相应的reduce task,不断地合并数据,为reduce的数据输入做准备,当所有的map tesk完成后,数据也拉取合并完毕后,reduce task 启动,最终将输出输出结果存入HDFS上。
(五) MapReduce的执行步骤:
1、Map任务处理
  1.1 读取HDFS中的文件。每一行解析成一个<k,v>。每一个键值对调用一次map函数。 <0,hello you> <10,hello me>
  1.2 覆盖map(),接收1.1产生的<k,v>,进行处理,转换为新的<k,v>输出。          <hello,1> <you,1> <hello,1> <me,1>
  1.3 对1.2输出的<k,v>进行分区。默认分为一个区。详见《Partitioner》
  1.4 对不同分区中的数据进行排序(按照k)、分组。分组指的是相同key的value放到一个集合中。 排序后:<hello,1> <hello,1> <me,1> <you,1> 分组后:<hello,{1,1}><me,{1}><you,{1}>
  1.5 (可选)对分组后的数据进行归约。详见《Combiner》
2、Reduce任务处理
  2.1 多个map任务的输出,按照不同的分区,通过网络copy到不同的reduce节点上。(shuffle)详见《shuffle过程分析》
  2.2 对多个map的输出进行合并、排序。覆盖reduce函数,接收的是分组后的数据,实现自己的业务逻辑, <hello,2> <me,1> <you,1>
    处理后,产生新的<k,v>输出。
  2.3 对reduce输出的<k,v>写到HDFS中。
(六)针对MapReduce的缺点,YARN解决了什么?
MapReduce由以下缺点:
★ JobTracker挂掉,整个作业挂掉,存在单点故障
★ JobTracker既负责资源管理又负责作业控制,当作业增多时,JobTracker内存是扩展的瓶颈
★ map task全部完成后才能执行reduce task,造成资源空闲浪费
YARN设计考虑以上缺点,对MapReduce重新设计:
★ 将JobTracker职责分离,ResouceManager全局资源管理,ApplicationMaster管理作业的调度
★ 对ResouceManager做了HA设计
★ 设计了更细粒度的抽象资源容器Container
(七)Java代码实现
1)MapReduce编程主要组件
InputFormat类:分割成多个splits和每行怎么解析。
Mapper类:对输入的每对<key,value>生成中间结果。
Combiner类:在map端,对相同的key进行合并。
Partitioner类:在shuffle过程中,将按照key值将中间结果分为R份,每一份都由一个reduce去完成。
Reducer类:对所有的map中间结果,进行合并。
OutputFormat类:负责输出结果格式。

编程框架如下:

Mapper------map
		public static class TokenizerMapper 
		extends Mapper<Object, Text, Text, IntWritable>{
			map方法
		}
		Reducer-----reduce
		public static class IntSumReducer 
       extends Reducer<Text,IntWritable,Text,IntWritable> {
			reduce的方法
		}
		main-----主方法入口
		{
			组装map和reduce   并进行运行提交
		}

单词统计实例:

import java.io.IOException;
import java.io.Serializable;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
/**
 * 单词统计
 */
import org.apache.hadoop.mapreduce.Mapper;
/**
 * 输入和输出   map类型
 * KEYIN, 输入的键的类型   这里指的是每一行的起始偏移量  long  0   12
 * VALUEIN,输入的value的类型  这里指的是每一行的内容   和偏移量一一对应的   String
 * 输出的类型取决于  业务
 *  KEYOUT,  输出的键的类型  这里指的每一个单词   ---  string
 *  VALUEOUT,输出的值的类型  这里指的单词的次数  ----  int
 * @author lv_hulk
 * 
 * 
 * 这里的数据类型  不能使用java的原生类型
 * 	序列化:数据持久化存储  或  网络传输的时候  数据需要序列化和反序列化的
 * 	张三---序列化------010101110-----反序列化-----张三
 * java-----Serializable
 * mapreduce编程中的用于传输的数据类型必须是序列化和反序列化能力的
 *   hadoop弃用了java中原生的Serializable   实现的自己的一套序列化和反序列化的 接口Writable  只会对数据的值进行序列化和反序列化
 *   原因:java中的序列化和反序列化太重    繁琐
 *   Long   1   
 *   对于一些常用的数据类型  hadoop帮我们实现好了:
 *   int------intWritable
 *   long----LongWritable
 *   string-----Text
 *   byte------ByteWritable
 *   double----DoubleWritable
 *   float-----FloatWritable
 *   boolean-----BooleanWritable
 *   null-----NullWritable
 *自己定义的需要序列化和反序列化  实现  Writable接口
 */
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
	//重写map函数
	/**
	 * 参数:
	 * hadoop的底层数据读取的时候   字节读取的
	 * LongWritable key:输入的key   这里指的是每一行的偏移量   没有实际作用  一行的标识而已
	 * Text value:输入的value  这里指的是一行的内容
	 * Context context:上下文对象   用于传输     传输reduce中
	 * 函数的调用频率:
	 * 	一行调用一次
	 * 如果一个文件  10行-----map函数会被调用10次
	 */
	@Override
	protected void map(LongWritable key, 
			Text value, 
			Context context)
			throws IOException, InterruptedException {
		
		
		
		//创建一个流   进行读取(mapreduce框架帮你做了)    每一行内容进行切分
		//获取每一行内容  进行切分
		//text--toString()--String
		String line = value.toString();
		//进行切分   hello	word	hello	ww
		String[] words = line.split("\t");
		//循环遍历每一个单词  进行统计    直接发送到reduce端   发送的时候  k-v
		for(String w:words){
			//将String---Text
			Text mk=new Text(w);
			//int----IntWritable
			IntWritable mv=new IntWritable(1);
			//hello,1    world,1     hello,1    ww,1
			//这里write是直接写出  调用一次  就会写出一个   k---v写出reduce端
			context.write(mk, mv);
		}
		
	}
	
}

import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
/**
 * 单词统计
 */
import org.apache.hadoop.mapreduce.Reducer;
/**
 * reduce的数据来源于map
 * @author lv_hulk
 *KEYIN,  输入的key的类型    这里指的是map输出key类型   Text
 * VALUEIN, 输入的value的类型  这里指的是map输出的value的类型    IntWritable
 * 
 * 输出
 *KEYOUT, 输出的key    这里指的单词的类型  Text
 *VALUEOUT,输出的value的类型   这里指的是单词的总次数    IntWritable
 */
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable>{
	//重写  reduce方法
	/**
	 * 到reduce端的数据  是已经分好组的数据
	 * 默认情况下   按照map输出的key进行分组  将map输出的key相同的分为一组
	 * Text key,    每一组中的相同的key
	 * Iterable<IntWritable> values,  每一组中的所有的value值  封装到一个迭代器中了
		Context context:上下文对象   用于传输的  写出到hdfs中
		
		reduce调用频率:
			一组调用一次  每次只统计一个单词最终结果
		每一组只能访问本组的数据  没有办法和上一组的额数据  下一组的数据共享的
	 */
	@Override
	protected void reduce(Text key, Iterable<IntWritable> values,
			Context context) throws IOException, InterruptedException {
		//循环遍历values  求和
		int sum=0;
		for(IntWritable v:values){
			//intwritable---int  数值类型   get   将hadoop中的类型转换为java中的类型
			sum+=v.get();
		}
		//写出结果文件
		IntWritable rv=new IntWritable(sum);
		context.write(key, rv);
	}

}

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.output.FileOutputFormat;

/**
 * 驱动类
 * @author lv_hulk
 *
 */
public class Driver {
	public static void main(String[] args) {
		//将mapper  reducer类进行一个封装  封装为一个任务----job(作业)
		//加载配置文件
		Configuration conf=new Configuration();
		//启动一个Job  创建一个job对象
		try {
			Job job=Job.getInstance(conf);
			//设置这个job
			//设置整个job的主函数入口
			job.setJarByClass(Driver.class);
			
			//设置job的mappper的类
			job.setMapperClass(WordCountMapper.class);
			
			//设置job的reducer的类
			job.setReducerClass(WordCountReducer.class);
			
			
			//设置map输出key   value的类型
			//指定了泛型  这里为什么还要设置一次   泛型的作用范围  编译的时候生效   运行的时候泛型会自动擦除
			job.setMapOutputKeyClass(Text.class);
			job.setMapOutputValueClass(IntWritable.class);
			
			//设置reduce的输出的k   v类型  以下方法设置的是mr的最终输出
			job.setOutputKeyClass(Text.class);
			job.setOutputValueClass(IntWritable.class);
			
			
			//设定切片大小  小于128M
			//设置   mapreduce.input.fileinputformat.split.maxsize  最大的切片的大小
			FileInputFormat.setMaxInputSplitSize(job, 1*1024);
			//设置  mapreduce.input.fileinputformat.split.minSize
			//FileInputFormat.setMinInputSplitSize(job, size);
			
			//指定需要统计的文件的输入路径  FileInputFormat  文件输入类
			Path inpath=new Path(args[0]);
			FileInputFormat.addInputPath(job, inpath);
			
			//指定输出目录  输出路径不能存在的  否则会报错  默认输出是覆盖式的输出  如果输出目录存在  有可能造成原始数据的丢失
			Path outpath=new Path(args[1]);
			FileOutputFormat.setOutputPath(job, outpath);
			
			//提交job  执行这一句的时候 job才会提交  上面做的一系列的工作  都是设置job
			//job.submit();
			job.waitForCompletion(true);
			
		} catch (Exception e) {
			
			e.printStackTrace();
		}
		
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值