MapReduce2.0详解

MapReduce是一种分布式运算程序的编程框架,将业务逻辑代码整合成完整的分布式程序运行于Hadoop集群。采用多进程并发,便于任务控制。核心流程包括:MRAppMaster启动,计算Maptask数量,启动Maptask进程处理数据切片;Maptask读取数据,执行map()方法,输出K-V对;ReduceTask拉取Maptask输出,进行归并排序和reduce()方法运算。

一个分布式运算程序的编程框架。

核心功能:将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个hadoop集群上。

MapReduce采用的是多进程并发方式,而不是多线程并发。

优点:方便对每一个任务进行控制和调配。

缺点:进程相对线程来说会消耗更多的启动时间。

总体流程(MR2.0):

1、一个mr程序启动的时候,最先启动的是MRAppMaster,MRAppMaster启动后根据本次job的业务描述信息,计算出需要的Maptask实例数量,即将作为输入的HDFS上的文件切分形成输入分片(inputSplit),为了切分句子的完整性,每个inputSplit包含后一个block的开头部分每个inputSplit将作为一个map任务的输入,根据分片数量然后向集群申请机器启动相应数量的Maptask进程。inputSplit的大小和数量对于mapreduce作业的性能有非常大的影响。

(注意:①inputSplit只是逻辑上对输入数据进行分片,并不会将文件在磁盘上切成分片进行存储。inputSplit只记录了分片的元数据节点信息,例如起始位置、长度以及所在节点列表等;压缩文件不可切分,且一个压缩文件对应一个maptask;只有非压缩文件和sequence文件可以切分,所以,压缩文件是一个控制maptask个数的很好方法

 

 

 

2、Maptask进程启动之后,根据给定的数据切片范围进行数据处理,主体流程为:

a)利用客户指定的InputFormat来获取RecordReader读取数据,形成输入K-V对

注意:InputFormat主要有两个功能:1)数据切分;2)记录读取器-RecordReader)

b)将输入K-V对传递给客户定义的map()方法做逻辑运算,也即是每读取一条记录,调用一次map()方法。并将map()方法输出的K-V对收集到缓存,每个map端有一个环形内存缓冲区,用于存储任务的输出。默认大小100MB(io.sort.mb属性),一旦达到阀值0.8(io.sort.spill.percent),一个后台线程就把内容写到(spill)Linux本地磁盘中的指定目录(mapred.local.dir)下的新建的一个溢出写文件。

注意map过程的输出是写入本地磁盘而不是HDFS

环形内存缓冲区的作用:减少磁盘I/O的开销,提高合并和排序的速度。在编写map函数的时候要尽量减少内存的使用,为shuffle过程预留更多的内存,因为该过程是最耗时的过程。)

c)将缓存中的K-V对按照K分区排序后不断溢写到磁盘文件,写磁盘前,要进行partition、sort和combine等操作。这里的patition分区默认是HashPartitioner通过分区,将不同类型的数据分开处理,之后对不同分区的数据进行排序,如果有Combiner,还要对排序后的数据进行combine。等最后记录写完,将全部溢出文件合并为一个分区且排序的文件。

注意在写磁盘的时候采用压缩的方式将map的输出结果进行压缩是一个有效减少网络开销的方法!默认情况下,map的输出是不压缩的,只要在mapred-site.xml文件的配置项mapreduce.cpmpress.map.output设为true即可开启压缩功能,使用的压缩库由mapreduce.map.output.compression.codec指定)

3、MRAppMaster监控到所有Maptask进程任务完成之后,会根据客户指定的参数启动相应数量的Reducetask进程,并告知Reducetask进程要处理的数据范围(数据分区)

4、Reducetask进程启动之后,根据MRAppMaster告知的待处理数据所在位置,从若干台Maptask运行所在机器上的若干个Maptask输出结果文件拉取数据,这其实就是一个copy过程。Reducetask进程启动一些copy线程(Fetcher),通过HTTP方式请求maptask获取输出文件。并在本地进行重新归并排序,即进入merge阶段。这里的merge如map端的merge动作,只是数组中存放的数值是不同map端copy来的。copy过来的数据会先放入内存缓冲区中,这里的缓冲区大小要比map端的更为灵活,它基于JVM的heap size设置,因为Shuffle阶段Reducer不运行,所以应该把绝大部分的内存都给Shuffle用。这里需要强调的是,merge有三种形式:1)内存到内存  2)内存到磁盘  3)磁盘到磁盘。默认情况下第一种形式不启用,当内存中的数据量到达一定阈值,就启动内存到磁盘的merge。与map 端类似,这也是溢写的过程,然后在磁盘中生成了众多的溢写文件。第二种merge方式一直在运行,直到没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成一个最终文件。然后按照相同key的K-V为一个组,调用客户定义的reduce()方法进行逻辑运算,对相同分区的文件调用一次reduce()函数,并收集运算输出的结果K-V,然后调用客户指定的outputformat将结果数据输出到本地或HDFS上。

在map任务和reduce任务的过程中,一共发生了3次排序操作:

1、当map函数产生时,会首先写入内存的环形缓冲区,在达到设定阈值溢写磁盘之前,后台线程会将缓存区的数据分成相应的分区,在每个分区中,后台线程按键进行内排序。

2、map任务完成之前,磁盘上存在多个已经分区好、排序好、大小和缓冲区一样的溢写文件,这时溢写文件将被合并成一个已分区且已排序的输出文件。

3、在shuffle阶段,需要将多个map任务的输出文件合并,归并排序。

Combiner的执行时机:

Combiner会在map端的那个时期执行呢?实际上,Conbiner函数的执行时机可能会在map的merge操作完成之前,也可能在merge之后执行,这个时机由配置参数min.num.spill.for.combine(该值默认为3),也就是说在map端产生的spill文件最少有min.num.spill.for.combine的时候,Conbiner函数会在merge操作合并最终的本机结果文件之前执行,否则在merge之后执行。通过这种方式,就可以在spill文件很多并且需要做conbine的时候,减少写入本地磁盘的数据量,同样也减少了对磁盘的读写频率,可以起到优化作业的目的。

MapTask并行度(个数)决定机制:

一个job的mapTask并行度由客户端在提交job时决定。

将待处理数据执行逻辑切片,即按照一个特定切片大小,将待处理数据划分成逻辑上的多个split,然后每一个split分配一个mapTask并行实例处理

注意block是HDFS上物理上存储的存储的数据,切片是对数据逻辑上的划分。两者之间没有关系。即使HDFS上是128M存储,Mapreduce也会切片,只是默认切片也是128M。也可以非128M切片,如100M,多余的部分由框架内部处理和其他结点进行拼接切片。因为切片数量决定了mapTask进程数量

block大小:Hadoop 2.x默认的block大小是128MB,Hadoop 1.x默认的block大小是64MB,可以在hdfs-site.xml中设置dfs.block.size,注意单位是byte。

split分片大小:范围在mapred-site.xml中设置,mapred.min.split.size mapred.max.split.size,minSplitSize大小默认为1B,maxSplitSize大小默认为Long.MAX_VALUE = 9223372036854775807。那么split分片到底是多大呢?

minSize=max{minSplitSize,mapred.min.split.size}

maxSize=mapred.max.split.size

splitSize=max{minSize,min{maxSize,blockSize}}

所以在我们没有设置分片范围的时候,分片大小是由block块大小决定的,和block块的大小一样。比如把一个258MB的文件上传到HDFS上,假设block块大小是128MB,那么它就会被分成三个block块,与之对应产生三个split,所以最终会产生三个maptask进程。那第三个block块里存的文件大小只有2MB,而它的block块大小是128MB,那么它实际占用Linux file system的多大空间?答案是实际的文件大小2MB,而非一个块的大小。

那么如果hdfs占用Linux file system的磁盘空间按实际文件大小算,这个“块大小”有必要存在吗?其实块大小还是必要的,一个显而易见的作用就是当文件通过append追加操作不断增长的过程中,可以通过block size来决定何时split文件。

MapTask 数量:hadooop提供了一个设置map进程个数的参数mapred.map.tasks,我们可以通过这个参数来控制map的个数。但是通过这种方式设置map的进程个数,并不是每次都有效的。原因是mapred.map.tasks只是一个hadoop的参考数值,最终map的进程个数,还取决于很多因素。如mapreduce还要遵循一些原则, mapreduce的每一个map进程处理的数据是不能跨越文件的,也就是说min_map_num >= input_file_num。

默认个数:default_num = total_size / block_size;

期望个数:goal_num = mapred.map.tasks; (只有goal_num大于default_num时才会生效)

分片个数:split_num = total_size / split_size;(只有split_size大于block_size时才会生效

计算个数:compute_map_num = min(split_num,  max(default_num, goal_num))

最终个数:final_map_num = max(compute_map_num, input_file_num)

MapTask进程数量该怎么控制?

1.如果增加个数,则设置mapred.map.tasks 为一个较大的值。

2.如果减小个数,则设置mapred.min.split.size 为一个较大的值。

3.如果输入中有很多小文件,依然想减少map个数,则需要将小文件merger为大文件,然后参照2。

ReduceTask并行度决定机制:

Reducetask数量的决定是可以直接手动设置:

job.setNumReduceTasks(4);    // 默认值是1,手动设置为4

如果数据分布不均匀,就有可能在reduce阶段产生数据倾斜

注意: reducetask数量并不是任意设置,还要考虑业务逻辑需求,有些情况下,需要计算全局汇总结果,就只能有1个reducetask。尽量不要运行太多的reducetask。对大多数job来说,最好reduce的个数最多和集群中的reduce持平,或者比集群的 reduce slots小。

Shuffle详细过程:

1、maptask收集我们的map()方法输出的k-v对,放到内存缓冲区中,即环形缓冲区。

2、从内存缓冲区不断溢出本地磁盘文件,可能会溢出多个文件

3、多个溢出文件会被合并成大的溢出文件

4、在溢出过程中,及合并的过程中,都要调用partitoner进行分组和针对key进行排序

5、reducetask根据自己的分区号,去各个maptask机器上取相应的结果分区数据

6、reducetask会取到同一个分区的来自不同maptask的结果文件,reducetask会将这些文件再进行合并(归并排序)

7、合并成大文件后,shuffle的过程也就结束了,后面进入reducetask的逻辑运算过程(从文件中取出一个一个的键值对group,调用用户自定义的reduce()方法)

Shuffle中的环形缓冲区大小会影响到mapreduce程序的执行效率,原则上说,缓冲区越大,磁盘io的次数越少,执行速度就越快。缓冲区的大小可以通过参数调整。

参数:io.sort.mb  默认100M

输出压缩:

1、Mapreduce支持将map输出的结果或者reduce输出的结果进行压缩,以减少网络IO或最终输出数据的体积

2、压缩特性运用得当能提高性能,但运用不当也可能降低性能

3、基本原则:

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

IO密集型的job,多用压缩

Mapper输出压缩在配置参数中设置

mapreduce.compress.map.output=true

mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.GzipCodec

Reducer输出压缩在配置参数中设置

mapreduce.output.compress= true

mapreduce.output.compress.codec=org.apache.hadoop.io.compress. GzipCodec

hadoop支持的压缩格式

知识补充:

Map端k-v数据从环形缓冲区溢写到磁盘时的排序过程包括快速排序外部排序

外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。

快速排序之所比较快,因为相比冒泡排序,每次交换是跳跃式的。每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边。这样在每次交换的时候就不会像冒泡排序一样每次只能在相邻的数之间进行交换,交换的距离就大的多了。因此总的比较和交换次数就少了,速度自然就提高了。当然在最坏的情况下,仍可能是相邻的两个数进行了交换。因此快速排序的最差时间复杂度和冒泡排序是一样的都是O(N2),它的平均时间复杂度为O(NlogN)。其实快速排序是基于一种叫做二分的思想。快速排序的每一轮处理其实就是将这一轮的基准数归位,直到所有的数都归位为止,排序就结束了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值