来自:http://articles.e-works.net.cn/It_overview/Article103875.htm
Hadoop HDFS和MapReduce 架构浅析
前言
Hadoop是一个基于Java的分布式密集数据处理和数据分析的软件框架。Hadoop在很大程度上是受Google在2004年白皮书中阐述的MapReduce技术的启发。MapReduce工作原理是将任务分解为成百上千个小任务,然后发送到计算机集群中。每台计算机再传送自己那部分信息,MapReduce则迅速整合这些反馈并形成答案。简单来说,就是任务的分解和结果的合成。
Hadoop的扩展性非常优秀,Hadoop可处理分布在数以千计的低成本x86服务器计算节点中的大型数据。这种高容量低成本的组合引人注目,但Hadoop最吸引人的是其处理混合数据类型的能力。Hadoop可以管理结构化数据,以及诸如服务器日志文件和Web点击流的数据。同时还可以管理以非结构化文本为中心的数据,如Facebook和Twitter。
1 Hadoop基本架构
Hadoop 并不仅仅是一个用于存储的分布式文件系统,而是在由通用计算设备组成的大型集群上执行分布式应用的框架。Apache Hadoop项目中包含了下列产品(见图1)。
图1 Hadoop基本组成
Pig和Hive是Hadoop的两个解决方案,使得在Hadoop上的编程更加容易,编程人员不再需要直接使用Java APIs。Pig可加载数据、转换数据格式以及存储最终结果等一系列过程,从而优化MapReduce 运算。
Hive 在Hadoop 中扮演数据仓库的角色。Hive 可向HDFS添加数据,并允许使用类似SQL的语言进行数据查询。Chukwa是基于Hadoop集群的监控系统,简单来说就是一个WatchDog。HBase是一个面向列的分布式存储系统,用于在Hadoop中支持大型稀疏表的列存储数据环境。MapReduce用于超大型数据集的并行运算。HDFS 可以支持千万级的大型分布式文件系统。Zookeeper提供分布式应用程序的协调服务,支持的功能包括配置维护、名字服务、分布式同步、组服务等。Avro是一个数据序列化系统,用于支持大批量数据交换的应用。
Hadoop主要由HDFS和MapReduce引擎两部分组成。最底层是HDFS,它存储Hadoop集群中所有存储节点上的文件,HDFS的上一层是MapReduce引擎,该引擎由JobTracker和TaskTrackers组成。
2 HDFS 浅析
管理网络中跨多台计算机存储的文件系统称为分布式文件系统。HDFS以流式数据访问模式来存储超大文件,运行于商用硬件集群上。
HDFS的构建思路是这样的:一次写入、多次读取是最高效的访问模式。数据集通常由数据源生成或从数据源复制而来,接着长时间在此数据集上进行各类分析。每次分析会涉及该数据集的大部分数据甚至全部,因此读取整个数据集的时间延迟比读取第一条记录的时间延迟更重要。
所谓商用硬件,Hadoop并不需要运行在昂贵且高可靠的硬件上。对于庞大的集群来说,节点故障的几率还是非常高的。HDFS被设计成在遇到故障时能够继续运行且不让用户察觉到明显的中断。同时,商用硬件并非低端硬件。低端机器故障率远高于更昂贵的机器。当用户管理几十台、上百台,甚至几千台机器时,便宜的零部件故障率更高,导致维护成本更高。
HDFS是为高数据吞吐量应用优化的,这可能会以高时间延迟为代价。目前,对于低延迟的数据访问需求,HBase是更好的选择。
2.1 数据块
每个磁盘都有默认的数据块大小,这是磁盘进行数据读/写的最小单位。HDFS同样也有块的概念,默认为64 MB(很多情况下使用128 MB)。HDFS上文件也被划分为多个分块作为独立的存储单元。HDFS的块比磁盘块(一般为512字节)大很多,其目的是为了最小化寻址开销。但是该参数也不会设置得过大,MapReduce中的map任务通常一次处理一个块中的数据,因此如果tasks太少(少于集群中的节点数量),job的运行速度就会比较慢。
2.2 namenode和datanode
HDFS集群有两类节点,并分别以管理者(nameno?de)、工作者(datanode)模式运行。
namenode管理文件系统的命名空间,它维护着文件系统树及整棵树内所有的文件和目录。这些信息以2个文件(命名空间镜像文件和编辑日志文件)的形式永久保存在本地磁盘上。namenode也记录着每个文件中各个块所在的datanode信息,但它并不永久保存块的位置信息,因为这些信息会在系统启动时由datanode重建。同时,namenode也负责控制外部Client的访问。
datanode是文件系统的工作节点。它们根据需要存储并检索数据块(受客户端或namenode调度),响应创建、删除和复制数据块的命令,并且定期向nameno?de发送所存储数据块列表的“心跳”信息。HDFS内部的所有通信都基于标准的TCP/IP协议。namenode获取每个datanode的心跳信息,namenode据此验证块映射和文件系统元数据。
图2示出的是namenode和datanode。
图2 namenode和datanode
文件写入时的步骤为:
a)Client向namenode发起文件写入的请求。
b)namenode根据文件大小和文件块配置情况,将它管理的datanode节点的信息返回给Client。
c)Client将文件划分为多个块,根据datanode的地址信息,按顺序写入到每一个datanode块中。
文件读取时的步骤为:
a)Client向namenode发起文件读取的请求。
b)namenode返回存储文件的datanode的信息。
c)Client读取文件信息。
作为文件系统的管理员,没有namenode,文件系统将无法使用。如果运行namenode服务的机器毁坏,文件系统上所有的文件将会丢失,且不知道如何根据datanode的数据块来重建文件。Hadoop为此提供了2种机制对namenode实现冗余备份。
图3示出的是冗余namenode。
图3 冗余namenode
一种机制是备份保存文件系统元数据的文件。一般配置是:将文件系统元数据写入本地磁盘的同时,写入一个远程挂载的网络文件系统(NFS)。
另一种机制是运行一个辅助的namenode,但它不能被用作namenode。辅助的namenode通过编辑日志定期合并命名空间镜像。辅助namenode一般在另一台单独的物理计算机上运行,因为它需要占用大量CPU时间和与namenode相同容量的内存来执行合并操作。它会保存合并后的命名空间镜像的副本,并在namenode发生故障时启用。
但是,辅助namenode保存的状态总是滞后于主节点,一般情况融合2种机制。主namenode故障时,把存储在NFS上的namenode元数据复制到辅助namenode上,并将其作为新的主namenode运行。
2.3 命令行接口
HDFS 的文件和目录有与POSIX 相似的权限模式,通常是三类权限模式(rwx)。集群管理员可以通过命令行接口与HDFS交互,执行所有常见的文件系统操作,如创建目录、移动文件、删除数据、列出目录等等。HDFS并不是一个Unix文件系统,不支持像ls和cp这种标准的Unix文件命令。Hadoop提供了一套与Linux文件命令类似的命令行工具,通过shell命令操作文件和目录。
Hadoop也提供操作HDFS文件和目录的Java库,用于以编程方式访问HDFS。
一般情况下,由MapReduce框架读取HDFS文件和处理数据单元。除非需要定制数据的导入和导出,否则几乎不必编程来读写HDFS文件。
3 Hadoop MapReduce浅析
最简单的MapReduce 应用程序至少包含3 个部分:一个Map 函数、一个Reduce 函数和一个main 函数。main 函数将作业控制和文件输入/输出结合起来。在这点上,Hadoop提供了大量的接口和抽象类,从而为Hadoop应用程序开发人员提供许多工具,可用于调试和性能度量等。
MapReduce本身就是用于并行处理大数据的软件框架。MapReduce的根源是函数性编程中的Map函数和Reduce 函数。它由2 个可能包含许多事例(许多Map和Reduce)的操作组成。Map函数接受一组数据并将其转换为一个键/值对列表,输入域中的每个元素对应一个键/值对。Reduce函数接受Map函数生成的列表,然后根据它们的键(为每个键生成一个键/值对)缩小键/值对列表。可以在每个域上执行Map函数和Reduce函数,然后将输出的键/值对列表输入到另一个Reduce函数,就可得到与前面一样的结果。换句话说,可以在输入域并行使用相同的操作,得到的结果是一样的,但速度更快。MapReduce的并行功能可在任意数量的系统上使用。
图4示出的是MapReduce思想。
3.1 JobTracker和TaskTracker
Hadoop MapReduce 引擎由JobTracker 和Task?Tracker组成。图5示出的是Hadoop的结构。
JobTracker负责管理调度所有作业,它是整个系统分配任务的核心。与HDFS的namenode类似,Job?Tracker也是唯一的。它是Hadoop集群中唯一负责控制MapReduce应用程序的系统,在应用程序提交之后,将提供包含在HDFS中的输入和输出目录,JobTracker使用文件块信息(物理量和位置)确定如何创建其他TaskTracker从属任务,MapReduce应用程序被复制到每个出现文件块的节点,为特定节点上的每个文件块创建一个唯一的从属任务。
图4 MapReduce思想
图5 Hadoop的结构
TaskTracker具体负责执行用户定义的操作,每个任务被分割为任务集,包含Map任务和Reduce任务。任务是具体执行的基本单元,TaskTracker执行过程中需要向JobTracker发送心跳信息,汇报每个任务的执行状态,帮助JobTracker收集作业执行的整体情况,为下次任务的分配提供依据。
在Hadoop中,Client(任务的提交者)是一组API,用户需要自定义自己需要的内容,由Client将作业及其配置提交到JobTracker,并监控执行状况。
与HDFS的通信机制相同,Hadoop MapReduce也使用协议接口来实现服务器间的通信。Client与Task?Tracker及TaskTracker之间没有直接通信。由于集群各主机的通信比较复杂,点对点直接通信难以维持状态信息,所以由JobTracker收集整理统一转发。
3.2 MapReduce的工作机制
JobClient.runJob(conf)这一行简短的代码后面隐藏着大量的处理细节。整个过程如图6所示,包含如下4个独立的实体。
图6 运行MapReduce作业的工作原理
a)客户端:提交MapReduce作业。
b)JobTracker:协调作业的运行。
c)TaskTracker:运行作业划分后的任务。
d)分布式文件系统(一般为HDFS):用来在其他实体间共享作业文件。
3.2.1 作业的提交
JobClient的runJob()方法是用于新建JobClient实例并调用其submitJob()方法。提交作业后,runJob()每秒检测作业的进度,如果发现上次报告后有变化,便把进度报告给控制台。作业完成后,如果成功,就显示作业计数器。如果失败,导致作业失败的错误被记录到控制台。
JobClient的runJob()方法(图6步骤①)实现过程如下:
a)通过JobTracker的getNewJobId()方法,向Job?Tracker请求一个新的作业ID(图6步骤②)。
b)检查作业的输出说明。例如,如果没有指定输出目录或输出目录已经存在,作业就不提交,错误返回给MapReduce程序。
c)将运行作业所需要的资源(包括作业JAR 文件、配置文件和输入分片)复制到JobTracker文件系统中的一个以作业ID命名的目录下(图6步骤③)。作业JAR 的副本较多(由mapred.submit.replication 属性控制,默认值为10), 因此在运行作业的任务时,集群中有很多个副本可供TaskTracker访问。
d)通过调用JobTracker 的submitJob()方法告知JobTracker准备执行作业(图6步骤④)。
e)计算作业的输入分片。如果分片无法计算,例如,因为输入路径不存在,作业就不提交,错误返回给MapReduce程序(图6步骤⑥)。
3.2.2 作业的初始化
当JobTracker接收到对其submitJob()方法的调用后,会把此调用放入一个内部队列中,交由作业调度器(job scheduler)进行调度,并对其进行初始化。初始化包括创建一个表示正在运行作业的对象——封装任务和记录信息,以便跟踪任务的状态和进程(图6步骤⑤)。
为了创建任务运行列表,作业调度器首先从共享文件系统中获取JobClient已计算好的输入分片信息(图6步骤⑥)。然后为每个分片创建一个map任务。创建reduce 任务的数量由JobConf 的mapred.reduce.task属性决定,它是用setNumReduceTasks()方法来设置的,然后调度器创建相应数量的要运行的reduce任务。任务在此时被指定ID。
3.2.3 任务的分配
TaskTracker定期向JobTracker发送心跳。心跳告知JobTracker,TaskTracker是否还存活,同时也充当两者之间的消息通道。作为心跳的一部分,TaskTracker会指明它是否已经准备好运行新的任务。如果是,JobTracker会为它分配一个任务,并使用心跳的返回值与TaskTracker 进行通信(图6步骤⑦)。
在JobTracker 为TaskTracker 选择任务之前,JobTracker必须先选定任务所在的作业。一旦选择好作业,JobTracker就可以为该作业选定一个任务。
对于map任务和reduce任务,TaskTracker有固定数量的任务槽。例如,1个TaskTracker可能同时运行2个map 任务和2 个reduce 任务。准确数量由TaskTracker内核的数量和内存大小来决定。作业调度器在处理reduce任务槽之前,会填满空闲的map任务槽,因此如果TaskTracker至少有一个空闲的map任务槽,JobTracker会先为它选择一个map任务。
为了选择一个reduce任务,JobTracker简单地从待运行的reduce任务列表中选取下一个来执行,用不着考虑数据的本地化。然而,对于一个map任务,JobTracker会考虑TaskTracker的网络位置,并选取一个距离其输入分片文件最近的TaskTracker。
在最理想的情况下,任务是数据本地化的(data-local), 也就是任务运行在输入分片所在的节点上。同样,任务也可能是机架本地化的(rack-local)。任务和输入分片在同一个机架,但不在同一节点上。一些任务既不是数据本地化的,也不是机架本地化的,而是操作另一个机架上的数据。
3.2.4 任务的执行
现在,TaskTracker已经被分配了一个任务,下一步是执行该任务。第一步,通过从共享文件系统把作业的JAR文件复制到TaskTracker所在的文件系统,从而实现作业的JAR文件本地化。同时,TaskTracker将应用程序所需要的全部文件从共享文件系统复制到本地磁盘(图6步骤⑧)。第二步,TaskTracker为任务新建一个本地工作目录,并把JAR文件中的内容解压到这个文件夹下。第三步,TaskTracker新建一个TaskRunner实例来运行该任务。
TaskRunner启动一个新的JVM(图6步骤⑨)来运行每个任务(图6步骤⑩), 以便用户定义的map和re?duce 函数的任何软件问题都不会影响到TaskTracker(例如导致其崩坡或挂起等)。任务的子进程每隔几秒便告知父进程它的进度,直到任务完成。
3.2.5 进度和状态的更新
MapReduce作业是长时间运行的批量作业,这是一个很长的时间段,对于用户而言,能够得知作业进展是很重要的。一个作业和它的每个任务都有一个状态(status), 包括作业或任务的状态(如运行状态、成功完成、失败状态)、map和reduce的进度、作业计数器的值、状态信息或描述(可以由用户代码来设置)。
任务在运行时,对其进度保持追踪。对map任务,任务进度是已处理输入所占的比例。对reduce任务,情况稍微复杂,但系统仍然会估计已处理reduce输入的比例。比如,如果reduce任务已经执行reducer一半的输入,那么任务的进度便是5/6。因为已经完成复制和排序阶段(各1/3),并且已经完成reduce阶段的一半(1/6)。
如果任务报告了进度,便会设置一个标志以表明状态变化将被发送到TaskTracker。有一个独立的线程每隔3 s检查一次此标志,如果已设置,则告知Task?Tracker当前任务状态。同时,TaskTracker每隔5 s发送心跳到JobTracker(5 s这个间隔是最小值,心跳间隔实际上由集群的大小来决定,更大的集群,间隔会更长一些),并且将TaskTracker运行的所有任务的状态发送至JobTracker。
JobTracker将这些更新状态合并起来,生成一个表明所有运行作业及其所含任务状态的全局视图。同时,JobClient通过查询JobTracker来获取最新状态。客户端也可以使用JobClient的getJob()方法来得到一个RunningJob的实例,后者包含作业的所有状态信息。
3.2.6 作业的完成
当JobTracker收到作业最后一个任务已完成的通知后,便把作业的状态设置为“成功”。然后,在JobCli?ent查询状态时,便知道任务已经完成,于是JobClient打印一条消息告知用户,然后从runJob()方法返回。最后,JobTracker清空作业的工作状态,指示TaskTracker也清空作业的工作状态。
3.3 作业的调度
早期版本的Hadoop使用一种非常简单的方法来调度用户的作业。按照作业提交的顺序,即先进先出(FIFO)调度算法来运行作业。典型情况下,每个作业都会使用整个集群,因此作业必须等待直到轮到自己运行。虽然共享集群极有可能为多用户提供大量资源,但问题在于如何公平地在用户之间分配资源,这需要一个更好的调度器。
后来版本的Hadoop 加入设置作业优先级的功能。可以通过设置mapred.job.priority属性或JobClient的setJobPriority()方法来设置优先级。在这2种方法中,可以选择VERY_HIGH、HIGH、NORMAL、LOW、VERY_LOW中的一个值作为优先级。作业调度器会选择优先级最高的那个作业执行。
在Hadoop中,MapReduce的调度器可以选择。默认的调度器是FIFO,还可选择Fair Scheduler和Capaci?ty Scheduler。
Fair Scheduler的目标是让每个用户公平地共享集群能力。如果只有一个作业,它会得到集群的所有资源。随着提交的作业越来越多,空闲的TaskTracker任务槽会以“让每个用户公平共享集群”这种方式进行分配。即便一个用户的长时间作业正在运行而且还在进行过程中,另一个用户的一个短的作业会在合理的时间内完成。
作业都被放在作业池中,在默认情况下,每个用户都有自己的作业池。Fair Scheduler支持抢占,如果一个池在特定一段时间内未得到公平的资源,它会中止运行池中使用过多资源的任务,以便把任务槽让给运行资源不足的池。
针对多作业调度,Capacity Scheduler调度方式下,集群由很多队列组成,每个队列有一个分配能力。这一点与Fair Scheduler类似,只不过在每个队列内部,作业根据FIFO方式调度。即Capacity Scheduler允许为每个用户模拟一个独立的使用FIFO Scheduling的MapReduce集群。
4 应用场景及展望
云计算的伟大之处就在于在进行大数据处理时不必再像以往一样购买大量的服务器集群,租用服务器处理大数据更加利于控制成本。Hadoop作为一个重量级的分布式处理开源框架已经在大数据处理领域有所作为,企业希望利用Hadoop来规划其自身未来数据处理的蓝图。从EMC、Oracle到Microsoft,几乎所有高科技厂商都宣布了自己以Hadoop为基础的大数据战略。现今Hadoop已经成为IT商场吸引客户的热点词汇。