为什么要用MapReduce以及MapReduce的切片

MapReduce的由来

举一个熟悉的例子——WordCount,统计每个单词出现的次数。逻辑也非常简单——将文件中的每一行数据读取出来按照一定规则进行分割,然后把它放到一个HashMap里面,如果存在则value值+1,不存在则put进去并且value为1。

实现逻辑很简单,但是数据量一大的话,单机版的实现就不太现实,因为有以下几个问题:

1)文件的存储。单机的存储是有限的,而文件的大小可能是无上限的增长,例如日志文件,即使你按照每天输出,但是总的日志文件大小依然在持续增长,大小可能由2T到10T等等,所以单机是存不下来的,这个时候只能放在HDFS,因为HDFS上可以存放海量的大文件,比如HDFS上有100个节点,每个节点可以挂载8T的硬盘,那就有800T,每个文件存3个副本,按照每个文件2T的大小来说,至少可以存100多个这样的文件。

2)如果按照上面的步骤,将文件存储到HDFS上,每一个节点上都是文件的某些块,如果在这台机器上运行的话,你得到的结果也只是局部的数据,此时需要专门写一个客户端,程序在客户端上运行,客户端去所有节点上获取对应文件的所有块,读取一个块的数据就统计一个块的数据,这样子程序又变成了一个单机版的,但是这种实现下,内存就不够,因为需要保存一些中间数据。而且需要不断从网络里面去那些数据,也会很慢,所以这种实现方式不合理的。

你可能会想可不可以把程序分发到集群的每一个DataNode上去做统计,即把运算移动到数据上,而不是把数据移动到运算,数据在哪里,运算就在哪里。但是这样也有问题,运算变成了一个分布式的,每一份运算结果都是局部的结果,那么这个时候也存在问题:

1)如何实现代码分发到很多机器上运行、怎么配置启动环境、怎么启动起来。这个肯定需要一个庞大的系统去做,这个系统专门用于资源分发和Java启动程序,但是这个系统的开发难度、时间、所耗费的代价就很大。

2)数据放到HDFS上面,但是并不代表每一个DataNode上面都有这一部分数据里面的内容。因为集群很大,文件存进去的时候可能只占了其中的一部分节点。其他节点没有数据,如果把代码放在这些节点上可以运行,但是数据必须来源于网络,因此就造成了效率很低。因此代码应该发放到存放数据的节点上,但是这需要一个策略问题,解决这个策略问题后,你还需要考虑某台节点宕机的状况,这个时候的汇总结果就不对,而且你还需要实时监控程序运行情况,哪个节点正常,哪个不正常。

3)假设解决了上述的问题,但是每个节点的统计的数据只是中间结果,这个时候还是需要汇总,意味着要么把这些数据调到一台机器上进行汇总(所有单词都在一个机器上进行统计),要么将不同部分的数据在不同节点上汇总(这几个单词在这台机器上统计,其他单词在另外一台机器上统计)。如果是前者,汇总的那一台机器的负载会很高,如果后者,还需要写一个中间数据的调度系统。

就这样会发现一个简单的东西,把它变成分布式运行的程序,就会面临很多很多其他问题,跟我们的逻辑无关的问题,往往这些问题要比解决逻辑要复杂的多。

此时MapReduce就出现了,它将我们刚刚说的那些问题、逻辑都全部封装起来,我们只需要写自己逻辑即可。

MapReduce的认识 

在MapReduce中有一个基本思想——分而治之的思想。先将问题拆分处理,再合并统一进行最终的处理。因此在MapReduce中也分为了两个部分Map和Reduce。

1)Map:映射过程,把一组数据按照某种Map函数映射成新的数据,即把复杂的问题分解为若干个“简单的”问题。

2)Reduce:归约过程,把若干个组映射结果进行汇总并输出。

一个MapReduce作业(Job)包括三个部分:输入数据、MapReduce程序和配置信息。Hadoop将这些作业分成若干个任务task来执行,其中包括两类任务:map任务和reduce任务。任务运行在集群的节点上,并通过Yarn进行调度。如果一个任务失败,它将在另一个不同的节点上自动重新调度运行。

MapReduce的输入数据在进行运算之前,会进行一个分片(input split)的操作,此步骤会将输入数据划分成等长的小数据块。每一个分片会对应一个Map任务,并由该任务来运行用户自定义的Map函数从而处理分片中的每条记录。

1)为什么要进行切片操作?

不难知道,一台计算机处理每个分片的数据所需要的时间少于处理整个输入数据所花的时间。因此,如果并行处理每个分片,且每个分片数据比较小,那么整个处理过程将会获得更好的负载平衡。

2)分片的大小

一个MapReduce Job的执行时间也会与分片的大小息息相关。分片切得太小,管理分片的总时间和构件map任务的总时间将决定作业的整个执行时间。一般来说,一个合理的分片大小趋向于HDFS的一个块的大小,默认是128MB,不过可以手动设置这个默认值。之所以要设置和块保持一致的大小,是因为它是确保可以存储在单个节点上的最大输入块的大小,如果分片跨越两个数据块,那么对于任何一个HDFS节点,基本上都不可能同时存储这两个数据块。

Map任务一般都会在存储有数据的节点上运行,因为这样可以获得最佳性能,因为它无需使用宝贵的集群带宽资源。也就是所谓的“数据本地化优化”。但是,有时对于一个Map任务的输入分片来说,存储该分片的HDFS数据块副本的所有节点可能正在运行其他Map任务,此时作业调度需要从某一数据块所在机架中的节点上寻找一个空闲的Map槽来运行改Map任务分片。这种情况仅仅会在非常偶然的情况下发生,这将导致机架与机架之间的网络传输。

猜你喜欢

转载自blog.csdn.net/qq_35363507/article/details/113307668