易于编程、良好的扩展性、高容错、适合PB级以上海量数据的离线处理。
不擅长实时计算、不擅长流式计算、不擅长DAG(有向图)计算。
1.第一个阶段的map task并发实例,完全并行运行,互不相干。
2.第二个阶段的reduce task并发实例互不相干,但是他们的数据依赖于上一个阶段的所有map task并发实例的输出。
3.MapReduce编程模型只能包含一个map阶段和一个reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个mapreduce程序,串行运行。
每次切片时,都要判断切完剩下的部分是否大于块的1.1倍,不大于1.1倍就划分一块切片。
将输入目录下所有文件大小,依次和设置的setMaxInputSplitSize值比较,如果不大于设置的最大值,逻辑上划分一个虚拟存储块。如果输入文件大于设置的最大值且大于两倍,那么以最大值切割一个虚拟存储块;当剩余数据大小超过设置的最大值且不大于最大值2倍,此时将文件均分成2个虚拟存储块(防止出现太小切片)。之后判断虚拟存储的文件大小是否大于setMaxInputSplitSize值,大于等于则单独形成一个切片,如果不大于则跟下一个虚拟存储块进行合并,共同形成一个切片。
一个job的map阶段MapTask并行度(个数),由客户端提交job时的切片个数决定。
Read阶段:Map Task通过用户编写的RecordReader,从输入InputSplit中解析出一个个key/value。
Map阶段:该节点主要是将解析出的key/value交给用户编写map()函数处理,并产生一系列新的key/value。
Collect收集阶段:在用户编写map()函数中,当数据处理完成后,一般会调用context.write,context.write底层 OutputCollector.collect()输出结果。在该函数内部,会将生成的key/value分区(调用Partitioner),并写入一个环形内存缓冲区中。
Spill阶段:即“溢写”,当环形缓冲区满后,MapReduce会将数据写到本地磁盘上,生成一个临时文件。需要注意的是,将数据写入本地磁盘之前,先要对数据进行一次本地排序,并在必要时对数据进行合并等操作。
Combine阶段:当所有数据处理完成后,MapTask对所有临时文件进行一次合并,以确保最终只会生成一个数据文件。
在进行文件合并过程中,MapTask以分区为单位进行合并。对于某个分区,它将采用多轮递归合并的方式。每轮合并io.sort.factor(默认100)个文件,并将产生的文件重新加入待合并列表中。对文件排序后,重复以上过程,直到最终得到一个大文件。
系统执行排序的过程(即将mapper输出作为输入传给reducer)称为shuffle。
对于Map Task,它会将处理的结果暂时放到一个缓冲区中,当缓冲区使用率达到一定阈值后,再对缓冲区中的数据进行一次排序,并将这些有序数据写到磁盘上,而当数据处理完毕后,它会对磁盘上所有文件进行一次合并,以将这些文件合并成一个大的有序文件。
对于Reduce Task,它从每个Map Task上远程拷贝相应的数据文件,如果文件大小超过一定阈值,则放到磁盘上,否则放到内存中。如果磁盘上文件数目达到一定阈值,则进行一次合并以生成一个更大文件;如果内存中文件大小或者数目超过一定阈值,则进行一次合并后将数据写到磁盘上。当所有数据拷贝完毕后,Reduce Task统一对内存和磁盘上的所有数据进行一次合并。
1.Combiner是MR程序中Mapper和Reducer之外的一种组件。
2.Combiner组件的父类就是Reducer。
3.Combiner和reducer的区别在于运行的位置:
Combiner是在每一个Map Task所在的节点运行;
Reducer是接收全局所有Mapper的输出结果;
4.Combiner的意义就是对每一个maptask的输出进行局部汇总,以减小网络传输量。
Copy阶段:ReduceTask从各个MapTask上远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。
Merge阶段:在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。
Sort阶段:按照MapReduce语义,用户编写reduce()函数输入数据是按key进行聚集的一组数据。为了将key相同的数据聚在一起,Hadoop采用了基于排序的策略。由于各个MapTask已经实现对自己的处理结果进行了局部排序,因此,ReduceTask只需对所有数据进行一次归并排序即可。
Reduce阶段:reduce()函数将计算结果写到HDFS上。
Reducetask数量的决定是可以手动设置。
如果ReduceTask的数量 > getPartition(分区)的结果数,则会多产生几个空的输出文件;
如果1 < ReduceTask的数量 < getPartition的结果数,则有一部分分区数据无处安放,会Exception;
如果ReduceTask的数量 = 1,则不管mapTask端输出多少个分区文件,最终结果都交给这一个ReduceTask,最终也就只会产生一个结果文件。
压缩技术能够有效减少底层存储系统(HDFS)读写字节数,提高了网络带宽和磁盘空间的效率。
运算密集型的job,少用压缩
IO密集型的job,多用压缩
压缩格式 | 工具 | 算法 | 文件扩展名 | 是否可切分 |
---|---|---|---|---|
DEFLATE | 无 | DEFLATE | .deflate | 否 |
Gzip | gzip | DEFLATE | .gz | 否 |
bzip2 | bzip2 | bzip2 | .bz2 | 是 |
LZO | lzop | LZO | .lzo | 是 |
LZ4 | 无 | LZ4 | .lz4 | 否(不确定) |
Snappy | 无 | Snappy | .snappy | 否 |
当map任务输出的中间数据量很大时,应考虑在此阶段采用压缩技术,能显著改善内部数据Shuffle过程,而Shuffle过程在Hadoop处理过程中是资源消耗最多的环节。可用于压缩mapper输出的快速编解码器包括LZO、LZ4或者Snappy。
压缩reducer输出能够减少要存储的数据量,因此降低所需的磁盘空间。
MapReduce优化方法主要从六个方面考虑:数据输入、Map阶段、Reduce阶段、IO传输、数据倾斜问题和常用的调优参数。
合并小文件。
1.减少溢写(spill)次数:通过调整环形缓冲区的大小,增大触发spill的内存上限,减少spill次数,从而减少磁盘IO。
2.减少合并(merge)次数:通过调整io.sort.factor参数,增大merge的文件数目,减少merge的次数,从而缩短MR处理时间。
3.在map之后,不影响业务逻辑前提下,先进行Combine处理,减少 I/O。
1.合理设置maptask和reducetask数:两个都不能设置太少,也不能设置太多。太少,会导致task等待,延长处理时间;太多,会导致 map、reduce任务间竞争资源,造成处理超时等错误。
2.设置map、reduce共存:调整slowstart.completedmaps参数,使map运行到一定程度后,reduce也开始运行,减少reduce的等待时间。
3.合理设置reduce端的buffer:默认情况下,数据达到一个阈值的时候,buffer中的数据就会写入磁盘,然后reduce会从磁盘中获得所有的数据。也就是说,buffer和reduce是没有直接关联的,中间多个一个写磁盘->读磁盘的过程,既然有这个弊端,那么就可以通过参数来配置,使得buffer中的一部分数据可以直接输送到reduce,从而减少IO开销:mapred.job.reduce.input.buffer.percent,默认为0.0。当值大于0的时候,会保留指定比例的内存读buffer中的数据直接拿给reduce使用。这样一来,设置buffer需要内存,读取数据需要内存,reduce计算也要内存,所以要根据作业的运行情况进行调整。
使用自定义分区、使用Combine合并,如果有join尽量使用Map join。
原因:Reduce join方式中,合并的操作是在reduce阶段完成,reduce端的处理压力太大,map节点的运算负载则很低,资源利用率不高,且在reduce阶段极易产生数据倾斜(同一个reduce接收到的数据量很大)。