1.4 Hadoop与分布式开发
我们通常所说的分布式系统其实是分布式软件系统,即支持分布式处理的软件系统。它是在通信网络互联的多处理机体系结构上执行任务的系统,包括分布式操作系统、分布式程序设计语言及其编译(解释)系统、分布式文件系统和分布式数据库系统等。Hadoop是分布式软件系统中文件系统层的软件,它实现了分布式文件系统和部分分布式数据库系统的功能。Hadoop中的分布式文件系统HDFS能够实现数据在计算机集群组成的云上高效的存储和管理,Hadoop中的并行编程框架MapReduce能够让用户编写的Hadoop并行应用程序运行得以简化。下面简单介绍一下基于Hadoop进行分布式并发编程的相关知识,详细的介绍请参看后面有关MapReduce编程的章节。
Hadoop上并行应用程序的开发是基于MapReduce编程模型的。MapReduce编程模型的原理是:利用一个输入的key/value 对集合来产生一个输出的key/value 对集合。MapReduce库的用户用两个函数来表达这个计算:Map和Reduce。
用户自定义的Map函数接收一个输入的key/value 对,然后产生一个中间key/value 对的集合。MapReduce把所有具有相同key值的value集合在一起,然后传递给Reduce函数。
用户自定义的Reduce函数接收key和相关的value集合。Reduce函数合并这些value值,形成一个较小的value集合。一般来说,每次调用Reduce函数只产生0或1个输出的value值。通常我们通过一个迭代器把中间value值提供给Reduce函数,这样就可以处理无法全部放入内存中的大量的value值集合了。
图1-4是MapReduce的数据流图,体现MapReduce 处理大数据集的过程。简而言之,这个过程就是将大数据集分解为成百上千个小数据集,每个(或若干个)数据集分别由集群中的一个节点(一般就是一台普通的计算机)进行处理并生成中间结果,然后这些中间结果又由大量的节点合并,形成最终结果。图1-4也说明了MapReduce框架下并行程序中的两个主要函数:Map、Reduce。在这个结构中,用户需要完成的工作是根据任务编写Map和Reduce两个函数。
MapReduce计算模型非常适合在大量计算机组成的大规模集群上并行运行。图1-4中的每一个Map任务和每一个Reduce任务均可以同时运行于一个单独的计算节点上,可想而知,其运算效率是很高的,那么这样的并行计算是如何做到的呢?下面将简单介绍一下其原理。
- 数据分布存储
Hadoop分布式文件系统(HDFS)由一个名字节点(NameNode)和多个数据节点 (DataNode)组成,每个节点都是一台普通的计算机。在使用方式上HDFS与我们熟悉的单机文件系统非常类似,利用它可以创建目录,创建、复制、删除文件,并且可以查看文件内容等。但文件在HDFS底层被切割成了Block,这些Block分散地存储在不同的DataNode上,每个Block还可以复制数份数据存储在不同的DataNode上,达到容错容灾的目的。NameNode则是整个HDFS的核心,它通过维护一些数据结构来记录每一个文件被切割成了多少个Block、这些Block可以从哪些DataNode中获得,以及各个DataNode的状态等重要信息。 - 分布式并行计算
Hadoop中有一个作为主控的 JobTracker,用于调度和管理其他的TaskTracker。JobTracker可以运行于集群中的任意一台计算机上;TaskTracker则负责执行任务,它必须运行于DataNode上,也就是说DataNode既是数据存储节点,也是计算节点。JobTracker将Map任务和Reduce任务分发给空闲的TaskTracker,让这些任务并行运行,并负责监控任务的运行情况。如果某一个TaskTracker出了故障,JobTracker会将其负责的任务转交给另一个空闲的TaskTracker重新运行。 - 本地计算
数据存储在哪一台计算机上,就由哪台计算机进行这部分数据的计算,这样可以减少数据在网络上的传输,降低对网络带宽的需求。在Hadoop这类基于集群的分布式并行系统中,计算节点可以很方便地扩充,因此它所能够提供的计算能力近乎无限。但是数据需要在不同的计算机之间流动,故而网络带宽变成了瓶颈。“本地计算”是一种最有效的节约网络带宽的手段,业界将此形容为“移动计算比移动数据更经济”。 - 任务粒度
在把原始大数据集切割成小数据集时,通常让小数据集小于或等于HDFS中一个Block 的大小(默认是64MB),这样能够保证一个小数据集是位于一台计算机上的,便于本地计算。假设有M个小数据集待处理,就启动M个Map任务,注意这M个Map任务分布于N 台计算机上,它们将并行运行,Reduce任务的数量R则可由用户指定。 - 数据分割(Partition)
把Map任务输出的中间结果按key的范围划分成R份(R是预先定义的Reduce任务的个数),划分时通常使用Hash函数(如hash(key) mod R),这样可以保证某一段范围内的 key一定是由一个Reduce任务来处理的,可以简化Reduce的过程。 - 数据合并(Combine)
在数据分割之前,还可以先对中间结果进行数据合并(Combine),即将中间结果中有相同key的对合并成一对。Combine的过程与Reduce的过程类似,在很多情况下可以直接使用Reduce函数,但Combine是作为Map任务的一部分、在执行完Map函数后紧接着执行的。Combine能够减少中间结果中对的数目,从而降低网络流量。 - Reduce
Map任务的中间结果在执行完Combine和Partition之后,以文件形式存储于本地磁盘上。中间结果文件的位置会通知主控JobTracker,JobTracker再通知Reduce任务到哪一个TaskTracker上去取中间结果。注意,所有的Map任务产生的中间结果均按其key值通过同一个Hash函数划分成了R份,R个Reduce任务各自负责一段key区间。每个Reduce需要向许多个Map任务节点取得落在其负责的key区间内的中间结果,然后执行Reduce函数,形成一个最终的结果文件。 - 任务管道
有R个Reduce任务,就会有R个最终结果。很多情况下这R个最终结果并不需要合并成一个最终结果,因为这R个最终结果又可以作为另一个计算任务的输入,开始另一个并行计算任务,这也就形成了任务管道。
这里简要介绍了在并行编程方面Hadoop中MapReduce编程模型的原理、流程、程序结构和并行计算的实现,MapReduce程序的详细流程、编程接口、程序实例等请参见后面章节。