Hadoop--MapReduce详解(Map、Shuffle、Reduce)

目录

    • 1、MapReduce概述
      • 1.1 MapReduce是什么
      • 1.2 MapReduce核心思想和简单过程
      • 1.3 MapReduce的优缺点
    • 2、MapReduce工作机制
      • 2.1 MapReduce常用数据序列化类型
      • 2.2 MapReduce简单过程图示
        • 2.2.1 MapReduce进程
        • 2.2.2 数据切片与MapTask并行度
        • 2.2.3 分区机制与ReduceTask并行度
        • 2.2.4 排序
        • 2.2.5 MapReduce过程描述
      • 2.3 MapReduce工作机制
        • 2.3.1 MapTask工作机制
        • 2.3.2 ReduceTask工作机制
        • 2.3.3 Shuffle工作机制
    • 3、MapReduce编码规范
    • 4、简单示例----WordCount


1、MapReduce概述

1.1 MapReduce是什么

MapReduce是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架。

MapReduce是hadoop的核心组件之一,hadoop要实现分布式需要包括两部分,一部分是分布式文件系统 HDFS,一部分是分布式计算框架 MapReduce。MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上

1.2 MapReduce核心思想和简单过程

MapReduce计算框架用来处理海量数据,而一台计算机不可能计算如此大量的数据,一台不行那就使用多台一起处理数据。“ 分而治之 ”是MapReduce的核心思想。

MapReduce有两个过程:

  1. map阶段
    Map负责把一个任务分解成多个任务进行处理。任务分配等会增加程序的复杂性,但人多力量大,效率也会有明显地提高。

  2. reduce阶段
    Reduce负责对map阶段的结果进行汇总输出。

下面这个例子就很好的体现了MapReduce的思想

果园里有1000颗果树,现在要计算一共有多少个果子。

  1. 如果一个人算,就算这个人算的再快也一定要算好久,也有很大的可能 算着算着不想算罢工了 或者 超负荷去医院了;
  2. 但1000个人算的话,分配到每人身上的任务就只剩下一棵树了,虽然需要额外花点时间安排哪些人算哪些树和资源,但1000人同时开始,效率得到了极大的提高,最后将这1000个人所算的数进行汇总得到总果子数。

注:后面还将具体介绍MapReduce工作过程。

1.3 MapReduce的优缺点

优点

  1. MapReduce易于编程
    只需要实现一些简单接口,就可以完成一个分布式程序,这个分布式程序可以分布到大量廉价的PC机器上运行。也就是说你写一个分布式程序,就跟写一个简单的串行程序是一模一样的。

  2. 良好的扩展性(hadoop的特点)
    当你的计算资源不能满足的时候,你可以通过简单的增加机器(nodemanager)来扩展它的计算能力

  3. 高容错性
    MapReduce设计的初衷就是使程序能够部署在廉价的PC机器上,这就要求它具有很高的容错性,比如其中一台机器挂了,它可以把上面的计算任务转移到另外一个节点上运行,不至于整个任务运行失败。

  4. 适合PB级以上海量数据的离线处理
    可以实现上千台服务器集群并发工作,提供数据处理能力

缺点

  1. 不擅长实时计算
    MapReduce无法像MySQL一样,在毫秒或者秒级内返回结果

  2. 不擅长流式计算
    流式计算的输入数据是动态的,而MapReduce的输入数据集是静态的,不能动态变化。这是因为MapReduce自身的设计特点决定了数据源必须是静态的

  3. 不擅长DAG有向图计算
    多个应用程序之间存在依赖关系,后一个应用程序的输入为前一个程序的输出。在这种情况下,每个MapReduce作业的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常低下


2、MapReduce工作机制

2.1 MapReduce常用数据序列化类型

首先,先介绍MR的常用数据序列化类型,也为了方便后面的理解。
Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第1张图片
MR中,以 键值对(K-V) 的形式表示每个数据,而 K 和 V 的数据类型需要根据实即情况而定。框架把作业的输入(InPut)看成是一组键值对,同样也产生一组键值对作为作业的输出(PutOut),这两组键值对有可能是不同的。

Map和Reduce都要设置 输入 和 输出 数据类型;且Map的输出作为Reduce的输入 进行汇总,所以Map输出和Reduce输入必须一样。

程序的InPut就是Map的输入,程序的OutPut就是Reduce的输出。
Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第2张图片

2.2 MapReduce简单过程图示

这里是一个WordCount的例子,结合下面的一些概念和该图一起理解。
Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第3张图片

2.2.1 MapReduce进程

一个完整的MapReduce程序在分布式运行时有三类实例进程:

  1. Mr AppMaster:负责整个程序的过程调度及状态协调。
  2. MapTask:负责Map阶段的整个数据处理流程。
  3. ReduceTask:负责Reduce阶段的整个数据处理流程。

由上图可知,这个程序有3个MapTask 和 2个ReduceTask

2.2.2 数据切片与MapTask并行度

MapTask个数(并行度)等于切片数

  1. 一个Job的Map阶段并行度由客户端在提交Job时的切片数决定
  2. 每一个split切片分配一个MapTask并行实例处理
  3. 默认情况下,切片大小=BlockSize(老版本是64M,新版本是128M)
  4. 切片时不考虑数据集整体,而是逐个针对每一个文件单独切片

由上图可知,该程序一共有两个文件。第一个文件是200M,所以被切分成了两个切片;第二个文件是100M(小于128M),单独为一个切片。

2.2.3 分区机制与ReduceTask并行度

ReduceTask个数(并行度)默认等于ReduceTask的进程数

当自己设定分区数时
job.setNumReduceTasks(n):设置分区数
getPartition:自定义所取分区数

  1. 分区机制
    如果ReduceTask的数量 > getPartition的结果数,则会多产生几个空的输出文件part-r-000xxx;
    如果1 < ReduceTask的数量 < getPartition的结果数,则有一部分分区数据无处安放,报错;
    如果ReduceTask的数量=1,则不管MapTask端输出多少个分区文件,最终结果都交给这一个ReduceTask,最终只产生一个结果文件part-r-00000;
    分区号必须从零开始,逐一累加。
  2. 案例分析
    例如:假设自定义分区数为5,
    job.setNumReduceTasks(6);大于5,程序正常运行,多产生几个空的输出文件
    job.setNumReduceTasks(2);报错;
    job.setNumReduceTasks(1),正常运行,最终只产生一个输出文件;

由上图可知,该程序自定义分区,将把结果按单词首位(a-p)、(q-z)输出到两个文件中,ReduceTask个数默认为2。

2.2.4 排序

排序概述

  1. 排序是MapReduce框架中最重要的操作之一
  2. MapTask和ReduceTask均会对数据按照key进行排序。该操作是Hadoop的默认行为。任何应用程序中的数据均会被排序,而不是逻辑上是否需要。
  3. 默认排序是按照字典顺序排序,且实现该排序的方法是快速排序。

排序分类

  1. 部分排序:MapReduce根据输入记录的键对数据集排序,保障输出的每个文件内部有序。
  2. 全排序:最终输出结果只有一个文件,且文件内部有序。
  3. 辅助排序(GroupingComparator):在Reduce端对key进行分组
  4. 二次排序:在自定义排序过程中,如果compareTo中的判断条件为两个即为二次排序
2.2.5 MapReduce过程描述
  1. MapReduce运算程序一般需要分成2个阶段:Map阶段和Reduce阶段;
  2. Map阶段的并发MapTask,完全并行运行,互不相干;
  3. Reduce阶段的并发ReduceTask,完全互不相干,但是他们的数据依赖于上一个阶段的所有MapTask并发实例的输出;
  4. MapReduce编程模型只能包含一个Map阶段和一个Reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个MapReduce程序,串行运行。

注:MapTask如何工作、ReduceTask如何工作等若干问题细节下面会继续介绍

2.3 MapReduce工作机制

MapReduce详细工作流程
Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第4张图片
Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第5张图片

2.3.1 MapTask工作机制

过程详解

(1)Read阶段:MapTask通过用户编写的RecordReader,从输入InputSplit中解析出一个个key/value。
(2)Map阶段:该节点主要是将解析出的key/value交给用户编写map()函数处理,并产生一系列新的key/value。
(3)Collect收集阶段:在用户编写map()函数中,当数据处理完成后,一般会调用OutputCollector.collect()输出结果。在该函数内部,它会将生成的key/value分区(调用Partitioner),并写入一个环形内存缓冲区中。
(4)Spill阶段:即“溢写”,当环形缓冲区满后,MapReduce会将数据写到本地磁盘上,生成一个临时文件。在写入之前,先要对数据进行分区划分和一次本地排序

  • 分区划分。通过调用Partitioner的getPartition()方法就能知道该输出要送往哪个Reducer。默认的Partitioner使用Hash算法来分区,即通过key.hashCode() mode R来计算,R为Reducer的个数。getPartition返回Partition事实上是个整数,例如有10个Reducer,则返回0-9的整数,每个Reducer会对应到一个Partition。map输出的键值对,与partition一起存在缓冲中。

  • 对每部分分区的数据,使用快速排序算法(QuickSort)对key排序。并在必要时对数据进行合并(Combiner)、压缩(Compress

  • )等操作。
    Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第6张图片
    最终写入磁盘的是一个文件内部分区、区内部有序的临时文件
    Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第7张图片

(5)合并Spill文件阶段:当所有数据处理完后,MapTask会将所有临时文件合并成一个大文件,并保存到文件output/file.out中,同时生成相应的索引文件output/file.out.index。

  • 在进行文件合并过程中,MapTask以分区为单位进行合并。对于某个分区,它将采用多轮递归合并的方式。每轮合并io.sort.factor(默认10)个文件,并将产生的文件重新加入待合并列表中,对文件排序后,重复以上过程,直到最终得到一个大文件。

  • 让每个MapTask最终只生成一个数据文件,可避免同时打开大量文件和同时读取大量小文件产生的随机读取带来的开销。

最终每个MapTask只产生了一个文件
Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第8张图片

2.3.2 ReduceTask工作机制

过程详解
(1)Copy阶段:ReduceTask从各个MapTask上远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。
(2)Merge阶段:在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。
(3)Sort阶段:按照MapReduce语义,用户编写reduce()函数输入数据是按key进行聚集的一组数据。为了将key相同的数据聚在一起,Hadoop采用了基于排序的策略。由于各个MapTask已经实现对自己的处理结果进行了局部排序,因此,ReduceTask只需对所有数据进行一次归并排序即可,再以key进行分组。
(4)Reduce阶段:reduce()函数将计算结果写到HDFS上。

2.3.3 Shuffle工作机制

Map方法之后,Reduce方法之前的数据处理过程称之为Shuffle

实际上包括了前面所说的 Map阶段的数据溢写、分区、排序、合并;Reduce阶段的Copy、Merge、Sort。都已基本介绍。

这里再来简单介绍下shuffle中的两个可选优化

1、Combiner
本质:提前进行聚合,让MapTask分担ReduceTask的工作。

先讨论下面这个问题

  • 如果我们有60亿个数据,Mapper会生成60亿个键值对在网络间进行传输,但如果我们只是对数据求和,那么很明显的Mapper只需要输出它所知道的和即可。这样做不仅可以减轻网络压力,同样也可以大幅度提高程序效率。

Combiner目的:减少传输到Reduce中的数据量。为了削减Mapper的输出从而减少网络带宽和Reducer之上的负载。

对于Combiner有几点需要说明的是:

  • combine的输入和reduce的完全一致,输出和map的完全一致

  • 与combine与reducer不同的是,combiner没有默认的实现,需要显式的设置在conf中才有作用。combine是在每一个MapTask所在的节点运行;Reducer是接收全局所有Mapper的输出结果;

  • 并不是所有的job都适用combiner,只有操作满足结合律的才可设置combiner。combine操作类似于:opt(opt(1, 2, 3), opt(4, 5, 6))。如果opt为求和、求最大值的话,可以使用,但是如果是求中值的话,不适用。

  • 一般来说,combiner和reducer它们俩进行同样的操作。
    Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第9张图片

2、Compress
在进行网络传输前将数据压缩(如:ReuceCopyu数据前),接收数据后再进行解压,可以大大减少了磁盘IO以及网络IO;将Reduce数据的结果进行压缩,也能够很好的节省空间。

MR支持的压缩编码
Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第10张图片

注意:采用压缩技术减少了磁盘IO,但同时增加了CPU运算负担。所以,压缩特性运用得当能提高性能,但运用不当也可能降低性能。

压缩基本原则:

  • 运算密集型的job,少用压缩
  • IO密集型的job,多用压缩

3、MapReduce编码规范

用户编写的程序分成三个部分:Mapper、Reducer和Driver。

  1. Mapper阶段
    (1)用户自定义的Mapper要继承自己的父类
    (2) Mapper的输入数据是KV对的形式(KV的类型可自定义)
    (3) Mapper中的业务逻辑写在map()方法中
    (4) Mappen的输出数据是KV对的形式(KV的类型可自定义)
    (5) map()方法(MapTask进程)对每一个调用一次

  2. Reducer阶段
    (1)用户自定义的Reducer要继承自己的父类
    (2) Reducer的输入数据类型对应Mapper的输出数据类型,也是KV
    (3) Reducer的业务逻辑写在reduce()方法中
    (4) ReduceTask进程对每一组相同k的组调用一次reduce()方法
    (5) Reducer的输出数据类型也是KV

  3. Driver阶段
    相当于YARN集群的客户端,用于提交我们整个程序到YARN集群,提交的是封装了MapReduce程序相关运行参数的job对象


4、简单示例----WordCount

根据编码规范,要实现自定义Mapper、Reducer、Driver三个类

Mapper类

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

/*
 KEYIN      输入数据的key             表示每行数据的偏移量
 VALUEIN    输入数据的value			 表示每行数据
 KEYOUT		输出数据的key            表示每个输出数据的key
 VALUEOUT   输出数据的value          表示每个输出数据的key
 
 1、继承Mapper类
 2、指定以上四个参数类型
 
 */

public class WordcountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
	
	//输出的key  value
	Text k = new Text();
	IntWritable v = new IntWritable(1);
	
	@Override
	protected void map(LongWritable key, Text value, Context context)
			throws IOException, InterruptedException {
		
		//1. 读取每一行
		String line = value.toString();
		
		//2. 切割单词
		String[] words = line.split(" ");
		
		//3. 循环写出
		for (String word : words) {
			k.set(word);
			context.write(k, v); 
		}
	}
}

Reducer类

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
/*
 KEYIN      输入数据的key             
 VALUEIN    输入数据的value			 
 KEYOUT		输出数据的key            
 VALUEOUT   输出数据的value          
 
 1、继承Reducer类
 2、指定以上四个参数类型
 
 */
public class WordcountReducer extends Reducer<Text, IntWritable, Text, IntWritable>{
	
	IntWritable v = new IntWritable();
	int sum;
	
	  @Override
	protected void reduce(Text key, Iterable<IntWritable> values,
			Context context) throws IOException, InterruptedException {
		  
		  //you,1
		  //you,1
		  
		  sum = 0;
		  
		  //1. 累加求和
		  for (IntWritable value : values) {
			sum += value.get();
		  }
		  
		  //2. 写出
		  v.set(sum);
		  context.write(key, v);
	
		  //you,2
		  
	}

}

Driver

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;

public class WordcountDriver {

	public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

		args = new String[] { "D:\\Z-Test\\input\\InPutWordCount", "D:\\Z-Test\\output\\OutPutWordCount" };

		Configuration conf = new Configuration();

		// 1 获取配置信息以及封装任务job对象
		Job job = Job.getInstance(conf);

		// 2 设置jar加载路径
		job.setJarByClass(WordcountDriver.class);

		// 3 关联map和reduce类
		job.setMapperClass(WordcountMapper.class);
		job.setReducerClass(WordcountReducer.class);

		// 4 设置map输出kv类型
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(IntWritable.class);

		// 5 设置最终输出kv类型
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);

		// 6 设置输入和输出路径
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		FileOutputFormat.setOutputPath(job, new Path(args[1]));

		// 7 提交
		boolean result = job.waitForCompletion(true); // 为true 打印运行信息
		System.exit(result ? 0 : 1);
	}

}

InPutWordCount文件中有两个.txt文件
Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第11张图片
Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第12张图片
运行结果
Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第13张图片
由于没有进行自定义分区,这里默认只有一个分区,也就只有一个part-r-000000文件
Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第14张图片
统计成功
Hadoop--MapReduce详解(Map、Shuffle、Reduce)_第15张图片

你可能感兴趣的:(hadoop,hdfs,hadoop,大数据,其他,linux)