一、概述
1、定义
MapReduce是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架。
MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上。
2、优缺点
(1)优点
-
MapReduce易于编程:它简单的实现一些接口,就可以完成一个分布式程序,这个分布式程序可以分布到大量廉价的PC机器上运行。也就是说你写一个分布式程序,跟写一个简单的串行程序是一模一样的。就是因为这个特点使得MapReduce编程变得非常流行。
-
良好的扩展性:当你的计算资源不能得到满足的时候,你可以通过简单的增加机器来扩展它的计算能力。
-
高容错性:MapReduce设计的初衷就是使程序能够部署在廉价的PC机器上,这就要求它具有很高的容错性。比如其中一台机器挂了,它可以把上面的计算任务转移到另外一个节点上运行,不至于这个任务运行失败,而且这个过程不需要人工参与,而完全是由Hadoop内部完成的。
-
适合PB级以上海量数据的离线处理:可以实现上千台服务器集群并发工作,提供数据处理能力。
(2)缺点
-
不擅长实时计算:MapReduce无法像MySQL一样,在毫秒或者秒级内返回结果。
-
不擅长流式计算:流式计算的输入数据是动态的,而MapReduce的输入数据集是静态的,不能动态变化。这是因为MapReduce自身的设计特点决定了数据源必须是静态的。
-
不擅长DAG(有向无环图)计算:多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。在这种情况下,MapReduce并不是不能做,而是使用后,每个MapReduce作业的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常的低下。
3、核心编程思想
(1)分布式的运算程序往往需要分成至少2个阶段。
(2)第一个阶段的MapTask并发实例,完全并行运行,互不相干。
(3)第二个阶段的ReduceTask并发实例互不相干,但是他们的数据依赖于上一个阶段的所有MapTask并发实例的输出。
(4)MapReduce编程模型只能包含一个Map阶段和一个Reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个MapReduce程序,串行运行。
总结:分析WordCount数据流走向深入理解MapReduce核心思想。
4、进程
一个完整的MapReduce程序在分布式运行时有三类实例进程:
(1)MrAppMaster:负责整个程序的过程调度及状态协调。
(2)MapTask:负责Map阶段的整个数据处理流程。
(3)ReduceTask:负责Reduce阶段的整个数据处理流程。
二、序列化
- 序列化:序列化就是把内存中的对象,转换成字节序列(或其他数据传输协议)以便于存储到磁盘(持久化)和网络传输。一般来说,“活的”对象只生存在内存里,关机断电就没有了。而且“活的”对象只能由本地的进程使用,不能被发送到网络上的另外一台计算机。 然而序列化可以存储“活的”对象,可以将“活的”对象发送到远程计算机。
- 反序列化:反序列化就是将收到字节序列(或其他数据传输协议)或者是磁盘的持久化数据,转换成内存中的对象。
三、组成架构(重点)
1、原理
(1)总体流程
(2)详细工作流程
(3) MapTask工作机制
- Read阶段(1、2、3、4):MapTask通过InputFormat获得的RecordReader,从输入InputSplit中解析出一个个key/value。
-
Map阶段(5、6):该节点主要是将解析出的key/value交给用户编写map()函数处理,并产生一系列新的key/value。
-
Collect收集阶段(7、8):在用户编写map()函数中,当数据处理完成后,一般会调用OutputCollector.collect()输出结果。在该函数内部,它会将生成的key/value分区(调用Partitioner),并写入一个环形内存缓冲区中。
-
Spill阶段(9):即“溢写”,当环形缓冲区满后,MapReduce会将数据写到本地磁盘上,生成一个临时文件。需要注意的是,将数据写入本地磁盘之前,先要对数据进行一次本地排序,并在必要时对数据进行合并、压缩等操作。
-
Merge阶段(10):当所有数据处理完成后,MapTask对所有临时文件进行一次合并,以确保最终只会生成一个数据文件。
MapTask并行度由切片个数决定,切片个数由输入文件和切片规则决定。
(4)ReduceTask工作机制
- Copy阶段(13):ReduceTask从各个MapTask上远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。
-
Sort阶段(13、14):在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。按照MapReduce语义,用户编写reduce()函数输入数据是按key进行聚集的一组数据。为了将key相同的数据聚在一起,Hadoop采用了基于排序的策略。由于各个MapTask已经实现对自己的处理结果进行了局部排序,因此,ReduceTask只需对所有数据进行一次归并排序即可。
-
Reduce阶段:reduce()函数将计算结果写到HDFS上。
2、总体流程中的单个机制
(1)InputFormat数据输入
首先了解数据块和数据切片的概念:
- 数据块:Block是HDFS物理上把数据分成一块一块。数据块是HDFS存储数据单位。
-
数据切片:数据切片只是在逻辑上对输入进行分片,并不会在磁盘上将其切分成片进行存储。数据切片是MapReduce程序计算输入数据的单位,一个切片会对应启动一个MapTask。
其次,数据切片与MapTask并行度决定机制
最后,了解切片机制:
- FileInputFormat切片机制:简单按照文件的内容长度进行切片,切片大小默认等于Block大小,在切片时不考虑数据集整体而是逐个针对每一个文件单独切片
- TextInputFormat切片机制:一次读一行文本,然后将该行的起始偏移量作为key,行内容作为value返回。
- CombineTextInputFormat切片机制:将多个小文件合并成一个切片处理,提高处理效率
默认使用的实现类是:TextInputFormat
(2)shuffle机制
Map方法之后,Reduce方法之前的数据处理过程称之为Shuffle。
- MapTask收集我们的map()方法输出的kv对,放到内存缓冲区中
- 从内存缓冲区不断溢出本地磁盘文件,可能会溢出多个文件
- 多个溢出文件会被合并成大的溢出文件
- 在溢出过程及合并的过程中,都要调用Partitioner进行分区和针对key进行排序
- ReduceTask根据自己的分区号,去各个MapTask机器上取相应的结果分区数据
- ReduceTask会抓取到同一个分区的来自不同MapTask的结果文件,ReduceTask会将这些文件再进行合并(归并排序)
- 合并成大文件后,Shuffle的过程也就结束了,后面进入ReduceTask的逻辑运算过程(从文件中取出一个一个的键值对Group,调用用户自定义的reduce()方法)
注意:
- Shuffle中的缓冲区大小会影响到MapReduce程序的执行效率,原则上说,缓冲区越大,磁盘io的次数越少,执行速度就越快。
- 缓冲区的大小可以通过参数调整,参数:mapreduce.task.io.sort.mb默认100M。
其中涉及的内容包括:
-
Partition分区:将统计结果按照条件输出到不同文件中。
-
排序:MapTask和ReduceTask均会对数据按照key进行排序。该操作属于Hadoop的默认行为。任何应用程序中的数据均会被排序,而不管逻辑上是否需要。默认排序是按照字典顺序排序,且实现该排序的方法是快速排序。
(3)OutputFormat数据输出
OutputFormat是MapReduce输出的基类,所有实现MapReduce输出都实现了OutputFormat接口。默认实现类是TextOutputFormat,将每一个KV对,向目标文本文件输出一行。用户也可以自定义OutputFormat。
(4)join
1)Reduce Join
- Map端的主要工作:为来自不同表或文件的key/value对,打标签以区别不同来源的记录。然后用连接字段作为key,其余部分和新加的标志作为value,最后进行输出。
- Reduce端的主要工作:在Reduce端以连接字段作为key的分组已经完成,我们只需要在每一个分组当中将那些来源于不同文件的记录(在Map阶段已经打标志)分开,最后进行合并就ok了。
2)Map join
Map Join适用于一张表十分小、一张表很大的场景。MapJoin适用于关联表中有小表的情形。
在Map端缓存多张表,提前处理业务逻辑,这样增加Map端业务,减少Reduce端数据的压力,尽可能的减少数据倾斜。
(5)ETL
“ETL,是英文Extract-Transform-Load的缩写,用来描述将数据从来源端经过抽取(Extract)、转换(Transform)、加载(Load)至目的端的过程。ETL一词较常用在数据仓库,但其对象并不限于数据仓库
在运行核心业务MapReduce程序之前,往往要先对数据进行清洗,清理掉不符合用户要求的数据。清理的过程往往只需要运行Mapper程序,不需要运行Reduce程序。
3、总结(开发)
1)输入数据接口:InputFormat
- 默认使用的实现类是:TextInputFormat
- TextInputFormat的功能逻辑是:一次读一行文本,然后将该行的起始偏移量作为key,行内容作为value返回。
- CombineTextInputFormat可以把多个小文件合并成一个切片处理,提高处理效率。
2)逻辑处理接口:Mapper
用户根据业务需求实现其中三个方法:map() setup() cleanup ()
3)Partitioner分区
- 有默认实现 HashPartitioner,逻辑是根据key的哈希值和numReduces来返回一个分区号;key.hashCode()&Integer.MAXVALUE % numReduces
- 如果业务上有特别的需求,可以自定义分区。
4)Comparable排序
- 当我们用自定义的对象作为key来输出时,就必须要实现WritableComparable接口,重写其中的compareTo()方法。
- 部分排序:对最终输出的每一个文件进行内部排序。
- 全排序:对所有数据进行排序,通常只有一个Reduce。
- 二次排序:排序的条件有两个。
5)Combiner合并
Combiner合并可以提高程序执行效率,减少IO传输。但是使用时必须不能影响原有的业务处理结果。
6)逻辑处理接口:Reducer
用户根据业务需求实现其中三个方法:reduce() setup() cleanup ()
7)输出数据接口:OutputFormat
- 默认实现类是TextOutputFormat,功能逻辑是:将每一个KV对,向目标文本文件输出一行。
- 用户还可以自定义OutputFormat。
四、Hadoop数据压缩
1、压缩的好处和坏处
- 压缩的优点:以减少磁盘IO、减少磁盘存储空间。
- 压缩的缺点:增加CPU开销。
2、压缩原则
- 运算密集型的Job,少用压缩
- IO密集型的Job,多用压缩
3、压缩方式选择
压缩方式选择时重点考虑:压缩/解压缩速度、压缩率(压缩后存储大小)、压缩后是否可以支持切片。压缩可以在MapReduce作用的任意阶段启用。
- Gzip压缩:优点:压缩率比较高; 缺点:不支持Split;压缩/解压速度一般;
- Bzip2压缩:优点:压缩率高;支持Split;缺点:压缩/解压速度慢。
- Lzo压缩:优点:压缩/解压速度比较快;支持Split;缺点:压缩率一般;想支持切片需要额外创建索引
- Snappy压缩:优点:压缩和解压缩速度快;缺点:不支持Split;压缩率一般;