MapReduce简介

1.什么是MapReduce?
MapReduce做为hadoop三大核心组件之一,是一个分布式运算程序的编程框架,是用户开发“基于 hadoop 的数据分析应用”的核心框架。MapReduce 的核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,然后并发运行在一个hadoop 集群上。MapReduce是一种并行编程模型,用于大规模数据集的并行运算,它将复杂的、运行与大规模集群上的并行计算过程高度抽象到两个函数:Map和Reduce。极大的方便了分布式编程工作。
2.为什么要用MapReduce?
(1) 海量数据在单机上处理因为硬件资源限制,无法胜任;
(2) 一旦将单机版程序扩展到集群来分布式运行,将极大增加程序的复杂度和开发难度;
(3) 引入MapReduce框架后,开发人员可以将绝大部分工作集中在业务逻辑的开发上,而将分布式计算中的复杂性交由框架来处理从而提高开发效率,减轻开发者的负担。
3.MapReduce的优点
1.易于编程;
2.良好的扩展性;
3.高容错性;
4.适合PB级别以上的大数据的分布式离线批处理;
4.MapReduce的缺点
1.难以实时计算(MapReduce处理的是存储在本地磁盘上的离线数据);
2.不能流式计算(MapReduce设计处理的数据源是静态的);
3.难以DAG计算(MapReduce这些并行计算大都是基于非循环的数据流模型,也就是说,一次计算过程中,不同计算节点之间保持高度并行,这样的数据流模型使得那些需要反复使用一个特定数据集的迭代算法无法高效地运行);

MapReduce工作流程

MapReduce的核心思想可以用“分而治之”来描述。如下图所示。也就是把一个大的数据集差拆分成多个小数据块在多台机器上并行处理。也就是说,一个大的MapReduce作业,首先会被拆分成多个Map任务在多台机器上并行执行,每个Map任务通常运行在数据存储的节点上,这样,计算和数据就可以放在一起运行,不需要额外的数据传输开销。当Map任务结束后,会生成以<key,value>形式表示的许多中间结果。然后,这些中间结果会被分发到多个Reduce任务在多台机器上并行执行,具有相同key的<key,value>会被发送到同一个Reduce任务那里,Reduce任务会对中间结果进行汇总计算得到最后结果,并输出到分布式文件系统。

MapReduce中context的作用 mapreduce的作用是什么?_mapreduce

1.input
MapReduce需要把要执行的大文件数据进行切割(split),每一个输入分片(input split)对应一个map任务,输入分片(input split)存储的并非数据本身而是一个分片长度和一个记录数据位置的数组

输入分片(input split)和HDFS(hadoop2.0)之后的block(块)关系很密切,默认情况下切片大小等于分块大小。HDFS(hadoop2.0之后)的block块的大小默认是128M,如果我们执行的大文件是128x10M,MapReduce会分为10个map任务,每个map任务都存在于它所要计算的block(块)的DataNode上。

具体步骤

  1. 访问datanode中的数据反序列化数据并进行切片,为每一个切片分配一个map任务;
  2. 并发的执行这些任务;
  3. 通过recordReader读取切片中的每一条记录,按照记录格式读取,偏移值作为map的key,记录行作为value,当做map方法的参数。

2.map
对输入数据进行处理,调用map方法,将以上内容转换成真正的(key,value)的形式,然后调用context.write方法将该数据写出来。

3.shuffle
shuffle过程是MapReduce整个工作流程的核心环节,理解Shuffle过程的基本原理,对于理解MapReduce流程至关重要。所谓Shuffle,是指对Map输出结果进行分区、排序、合并等处理并交给Reduce的过程。
Map端的shuffle过程:Map的输入结果首先被写入缓存,当缓存满时,就启动溢血写操作,把缓存中的数据写入磁盘文件,并清空缓存。当启动溢写操作时,首先需要把缓存中的数据进行分区,然后对每个分区的数据进行排序和合并,之后再写入磁盘文件。每次溢写操作会生成一个新的磁盘文件。随着Map任务的执行,磁盘中就会生成多个溢写文件。在Map任务全部结束之前,这些溢写文件会被归并成一个大的磁盘文件,然后,通知相应的Reduce任务来领取属于自己处理的数据
Reduce端的Shuffle过程:Reduce任务从Map端的不同Map机器领会属于自己处理的那部分数据,然后,对数据进行归并(Merge)后交给Reduce处理。

具体过程
1.map数据会先输出到内存缓冲区中,到达默认的80%的阀值后,会像map任务本地写数据,每次写都会生成一个小文件;
2.在写到本地的过程中,会经历分区,排序,combiner(可选)的过程;
3.当最后一个文件溢写到本地磁盘中的时候,区与区的文件就是合并,排序,压缩(可选);
4.经过分区的排序的大文件会按照不同的分区被拷贝到相应的reduce中处理;
5.reduce端通过http network复制map端传来的数据;
6.将输入的数据排序后合并,经过2次排序后会返回一个value的迭代器;
7.分组将相同key的value放到一起作为一个value集合,作为reduce的输入。
注意事项
1.map任务的输出默认是放在本地磁盘的临时缓冲目录中的;
2.分区,排序,combiner过程可自定义;
3.由于受限于集群可用带宽,通常会对中间数据做压缩,combiner处理,减少网络带宽消耗;
4.分区的作用就是决定根据map输出的key值由哪个reduce处理;
5.mapper实现类读取配置文件属性,可以在配置中指定压缩的格式;
6.每一个分组的后台线程对输出结果的key进行排序,在排序的过程中,有combine函数则会进行调用。

4.reduce
处理<key,lis<>>对,对每个key产生一个结果。

5.output
对输出数据通常会做压缩,节省磁盘空间
将reduce结果按照输出的格式写入文件中

具体实现

Mapreduce是一个计算框架,既然是做计算的框架,那么表现形式就是有个输入(input),mapreduce操作这个输入(input),通过本身定义好的计算模型,得到一个输出(output),这个输出就是我们所需要的结果。
在运行一个mapreduce计算任务时候,任务过程被分为两个阶段:map阶段和reduce阶段,每个阶段都是用键值对(key/value)作为输入(input)和输出(output)。而程序员要做的就是定义好这两个阶段的函数:map函数和reduce函数。
Map函数的输入是来自于分布式文件系统的文件块,这些文件块的格式是任意的,可以文档,也可以是二进制格式的。文件块是一系列元素的集合,这些元素也是任意类型的,同一个元素不能跨文件块存储。Map函数将输入的元素转换成<key,value>形式的键值对,键和值得类型也是任意的。其中,键不同于一般的标志属性,即键没有唯一性,不能作为输出的身份标识,即使是同一元素,也可以通过一个Map任务生成具有相同键的多个<key,value>。
Reduce函数的任务就是将输入的一系列具有相同键的键值对以某种方式组合起来,输出处理后的键值对,输出结果会合并成一个文件。用户可以指定Reduce任务的个数,并通知实现系统,然后主控进程通常会选择一个Hsah函数,Map任务输出的每个键都会经过Hash函数计算,并根据哈希结果将该键值对输入相应的Redece任务来处理。对于处理键为k的Redecu任务的输入形式为<k,<v1,v2,…,vn>>,输出为<k,V>。

这里举例统计年份的最高气温

Map方法:

/**
 * 继承Mapper类
 *四个参数依次为输入的键值对和输出的键值对
 */
public class MpMapReduce extends Mapper<LongWritable, Text, Text, IntWritable> {

	// 定义常量
	private static final int MISSING = 9999;
	//实现一个map方法
	public void map(LongWritable key, Text value, Context context)
			throws IOException, InterruptedException {
	
		//转换text类型到String类型
		String line =  value.toString();
		
		//提取年份数据
		String year = line.substring(15,19);
		
		//气温变化
		int airTemperature;
		
		if(line.charAt(87) == '+') {
			airTemperature = Integer.parseInt(line.substring(88,92));
		}else {
			airTemperature = Integer.parseInt(line.substring(87,92));
		}
		
		String quality = line.substring(92,93);
		
		//将有效数据写入map的context中,注意类型务必要和泛型声明一致
		if(airTemperature != MISSING && quality.matches("{01459}")) {
			context.write(new Text(year), new IntWritable(airTemperature));
		}
	}

}

Reduce方法:

/**
 * 继承Reducer方法
 * @author admin
 *
 */
public class MpMapReducer extends Reducer<Text, IntWritable, Text, IntWritable> {

	//实现reduce方法
	protected void reduce(Text keyin, Iterable<IntWritable> valuein,
			Context context) throws IOException, InterruptedException {
		
		int maxValue = Integer.MIN_VALUE;
		
		//取最大值
		for(IntWritable value : valuein) {
			maxValue = Math.max(maxValue, value.get());
		}
		
		//将reduce的输出写入到context中
		context.write(keyin, new IntWritable(maxValue));
	}

}

入口方法,main函数

/**
 * App入口
 * @author admin
 *
 */
public class MaxTemperature {

	@SuppressWarnings("deprecation")
	public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
		//作业
		Job job = new Job();
		job.setJarByClass(MaxTemperature.class);
		//设置作业名称 便于调试
		job.setJobName("Max temperature");
		
		//添加输入路径 可以添加多个输入路径
		//输入路径不仅可以是具体文件  还可以是文件夹(目录,不会递归)
		FileInputFormat.addInputPath(job, new Path(args[0]));

		//设置输出路径 只能有一个输出路径 输出路径不可提前创建
		FileOutputFormat.setOutputPath(job, new Path(args[1]));
		
		//设置mapper类
		job.setMapperClass(MpMapReduce.class);
		//设置reducer类
		job.setReducerClass(MpMapReducer.class);
		
		//设置输出的key类型
		job.setOutputKeyClass(Text.class);
		//设置输出的value类型
		job.setOutputValueClass(IntWritable.class);
		
		//等待作业的完成
		job.waitForCompletion(true);
		System.out.println(job.waitForCompletion(true));
	}

}

Hadoop独立模式下运行程序:

1.导出jar包

把上面写好的3个Java文件导出jar包:

MapReduce中context的作用 mapreduce的作用是什么?_mapreduce_02


2.执行以下程序:

export HADOOP_CLASSPATH = MapReduce.jar//导出jar包的名字
hadopp MapReduce1.MaxTemperature xx.xx.xx xx.xx.xx //包名 输入文件路径 输出文件路径

Hadoop集群模式下运行程序:
在hdfs集群上执行程序:
1.将jar文件发送到Linux主机中;
2.将天气数据上传到hdfs文件系统;
3.执行指令:

hadoop jar xxx.jar /aa /xx //aa表示输入路径,xx输出路径