完全解析大数据中 MapReduce 的运行流程
为什么要学习 MapReduce
2004 年,谷歌发表了一篇名为《MapReduce》的论文,主要介绍了如何在分布式的存储系统上对数据进行高效率的计算。2005 年,Nutch 团队使用 Java 语言实现了这个技术,并命名为 MapReduce。时至今日,MapReduce 是 Apache Hadoop 的核心模块之一,是运行在 HDFS 上的分布式运算程序的编程框架,用于大规模数据集(大于 1TB)的并行运算。其中的概念,"Map(映射)"和"Reduce(归约)",是它们的主要思想,都是从函数式编程语言里借来的,还有从矢量编程语言里借来的特性。
●在过去的很长一段时间里,CPU 的性能都会遵循”摩尔定律“,在性能上每隔 18 个月左右就是提高一倍。那个时候,不需要对程序做任何改变,仅仅通过使用更优秀的 CPU,就可以进行性能提升。但是现在,在 CPU 性能提升的道路上,人类已经到达了制作工艺的瓶颈,因此,我们不能再把希望寄托在性能更高的 CPU 身上了。
●现在这个时候,大规模数据存储在分布式文件系统上,人们也开始采用分布式并行编程来提高程序的性能。分布式程序运行在大规模计算机集群上,集群是大量的廉价服务器,可以并行执行大规模数据处理任务,这样就获得了海量的计算能力
●分布式并行编程比传统的程序有明显的区别,它运行在大量计算机构成的集群上,可以充分利用集群的并行处理能力;同时,通过向集群中增加新的计算节点,就可以很容易地实现集群计算能力的扩展。
MapReduce 主要解决的是分布式文件存储系统上,数据的分布式计算的问题。在上述导读部分我们介绍过一个 WordCount 的案例,就是一个非常典型的分布式计算的案例。如果我们将所有的需要处理的数据移动到一个节点上进行处理,那么只是在数据传输的过程中就得消耗大量的时间,而且还可能在一台节点存不下这大量的数据。就算是能够存储下,也能够接受数据移动所带来的时间消耗,集群中其他节点的计算资源也都是在闲置的,不能高效率地利用集群。
因此我们就需要进行分布式的计算,将计算程序分发给不同的节点。在每一个节点上处理自己节点的数据,最后将每一个节点的数据处理结果汇总在一起。而在分布式计算的过程中会遇到很多的分布式计算的细节问题,这些问题都是需要开发人员去考虑的。那么如何去解决这些问题呢?
MapReduce 是一个开源的、分布式的计算框架,封装了分布式计算程序的实现细节,使得开发人员不需要了解分布式计算底层实现的情况下,就可以去开发一个分布式的计算程序。开发人员只需要将重心放在业务逻辑的实现即可,不需要关注分布式开发的底层细节。因此,对于开发人员来说,可以简化不少的工作量,提高程序开发的效率!
MapReduce 的核心思想 1、MapReduce 设计的一个理念是“计算向数据靠拢”(移动计算),而不是“数据向计算靠拢”(移动数据)
2、将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,移动到有数据存储的集群节点上,一是可以减少节点间的数据移动开销。二是在存储节点上可以并行计算,大大提高计算效率问题。 因为移动数据需要大量的网络传输开销,尤其是在大规模数据环境下,这种开销尤为惊人,所以移动计算要比移动数据更加经济。
3、MapReduce 一个完整的运算分为 Map 和 Reduce 两个部分。Map 会处理本节点的原始数据,产生的数据会临时存储到本地磁盘。Reduce 会跨节点 fetch 属于自己的数据,并进行处理,产生的数据会存储到 HDFS 上。
Hadoop 的 MapReduce 核心技术起源于谷歌在 2004 年发表的关于 MapReduce 系统的论文介绍。论文中有这么一句话:Our abstraction is inspired by the map and reduce primitives present in Lisp and many other functional languages。这句话提到了 MapReduce 思想来源,大致意思是,MapReduce 的灵感来源于函数式语言(比如 Lisp)中的内置函数 map(映射)和 reduce(规约)。
简单来说,在函数式语言里,map 表示对一个列表(List)中的每个元素做计算,reduce 表示对一个列表中的每个元素做迭代计算。它们具体的计算是通过传入的函数来实现的,map 和 reduce 提供的是计算的框架。我们想一下,reduce 既然能做迭代计算,那就表示列表中的元素是相关的(比如我想对列表中的所有元素做相加求和,那么列表中至少都应该是数值吧)。而 map 是对列表中每个元素做单独处理的,这表示列表中可以是杂乱无章的数据。这样看来,就有点联系了。在 MapReduce 里,Map 处理的是原始数据,自然是杂乱无章的,每条数据之间互相没有关系;到了 Reduce 阶段,数据是以 key 后面跟着若干个 value 来组织的,这些 value 有相关性,至少它们都在一个 key 下面,于是就符合函数式语言里 map 和 reduce 的基本思想了。
MapReduce 的运行流程
Map 阶段
1、MapTask 进程启动之后,会根据对应的输入分片来处理数据,每一个分片分配一个 MapTask 来处理。
2、使用客户端指定的 InputFormat 来读取数据,常用的就是 LineRecordReader 对象。它会逐行读取分片中的数据,并形成一个键值对。键为行偏移量,值为行记录。我们通常称为<K1, V1>
3、将读取到的<K1, V1>键值对数据输入到 map 方法中,进行用户自己的逻辑处理。并输出键值对,我们称为<K2, V2>
4、map 方法输出的<K2, V2>键值对,会被搜集进环形缓冲区。环形缓冲区是内存中的一个字节数组,默认的大小是 100M。
5、当环形缓冲区中被写入了 80%的数据的时候,会将这 80%的部分进行分区、排序,并溢写到磁盘,生成一个文件。在这个过程中,如果上游还有数据产生,将存储在剩余的 20%的空间内。如果上游产生数据的速度非常快,剩余的 20%也被填充满,并且 80%的数据还没有填写完成,此时任务将阻塞,上游不再读取数据,直到溢写文件完成。最终可能会生成多个溢写的磁盘文件。
6、将每一个溢写的磁盘文件合并成为一个文件,在合并的过程中,依然会按照分区、Key 进行排序。最终,每一个 MapTask 生成一个磁盘文件。至此,MapTask 执行结束。
Reduce 阶段
1、每一个 ReduceTask 需要去处理一个分区的数据,因此 ReduceTask 需要到每一个 MapTask 所在的节点去 fetch 自己分区的数据。
2、拉取到的数据,如果比较小,会直接保存在内存中,在内存中完成排序,直到达到内存阈值,将其填写到文件中;如果比较大,会直接以文件的形式保存起来。3、数据拉取完成后,会按照设定好的合并因子进行合并。
4、按照 Map 阶段输出的<K2, V2>的键进行分组,将相同键所对应的所有的值聚合到一起,形成一个集合。
5、将分组之后的键值对<K2, Iteratorable<V2>>,输入到 Reduce 方法中,进行逻辑处理。并输出最终的结果<K3, V3>6、将最终的结果,由客户端指定的 OutputFormat 进行输出。至此 ReduceTask 结束。
帮助到你的话就点个关注吧~
评论