Apache Hadoop最佳实践和反模式
Apache Hadoop是一个用于构建大规模,共享存储和计算基础设施的软件框架,Hadoop集群经常用于各种研究和开发项目,如Yahoo!,eBay,Facebook,Twitter等互联网公司就大量使用了Hadoop,并在核心业务系统中扮演中关键角色,因此正确部署Hadoop集群是确保获得最佳投资回报的关键。
本文介绍了在Apache Hadoop上运行应用程序的最佳实践,实际上,我们引入了网格模式(Grid Pattern)的概念,它和设计模式类似,它代表运行在网格(Grid)上的应用程序的可复用解决方案。
概述
Hadoop上的应用程序数据是使用Map-Reduce(映射-化简)范式写入的,Map-Reduce作业通常要将输入数据集拆分成独立的数据块,由Map任务以完全并行的方式处理,框架对Map的输出结果排序,然后传递给Reduce任务,通常情况下,作业的输入和输出结果都保存在文件系统上,框架管理计划任务,监控它们的执行情况,以及重新执行失败的任务。
Map-Reduce应用程序指定输入/输出位置,通过实现适当的Hadoop接口,如Mapper和Reducer,分别提供Map和Reduce功能,它们和其它作业参数一起构成作业配置。Hadoop作业客户端将作业(jar/可执行文件等)和配置提交给JobTracker,JobTracker承担起分配软件/配置,调度任务和监控的职责,为作业客户端提供状态和诊断信息。
Map/Reduce框架工作在(键/值)对上,也就是说,框架将给作业的输入看作是一对,并产生一对作为作业的输出,当然输入输出的类型可能是不同的。
下面是Map/Reduce应用程序中常见的数据流:
图1: Map/Reduce应用程序中的数据流
绝大多数Map-Reduce应用程序都在网格上执行,不会直接实现低级的Map-Reduce接口,相反,它们使用高级语言,如Pig实现。
Oozie是网格上完美的工作流管理和调度解决方案,它支持多种接口(Hadoop Map-Reduce,Pig,Hadoop Streaming和Hadoop Pipes等),并可以根据时间或数据可用性实现应用程序的调度。
网格模式
这部分内容涉及在网格上运行Map-Reduce应用程序的最佳实践。
输入
Hadoop Map-Reduce专门为处理大批量数据做了优化,Map通常使用并行方式处理数据,至少1个HDFS数据块,也就是说每次最少要处理128MB的数据。
◆默认情况下,这个框架每个Map至少要处理1个HDFS文件,这意味着如果某个应用程序要处理非常大的输入文件,最好是通过一种特殊的输入格式,如MultiFileInputFormat,让每个Map处理多个文件,即便是在处理为数不多的小型输入文件时也理应如此,每个Map处理多个文件可以大大提高效率。
◆如果应用程序需要处理大量的数据,即使它们存在于大型文件中,每个Map处理超过128MB的数据也会更快。
网格模式:在少量Map中聚合处理多个小型输入文件,使用更大的HDFS块大小处理超大型数据集。
Map(映射)
Map的数量通常是由输入的总大小决定的,即所有输入文件的总数据块数,因此,如果你要处理10TB输入数据,块大小128MB,那么总共需要82000个Map。
任务设置需要一段时间,因此执行大型作业时,Map至少需要一分钟。正如前面提到的,让每个Map同时处理多个文件效率会更高,因此,如果应用程序要处理超大型输入文件,让每个Map处理更大的数据块更有效,例如,让每个Map处理更多数据的一个方法是让应用程序处理更大的HDFS数据块,如512MB或尽可能更大。
作为一个极端的例子,Map-Reduce开发团队使用大约66000个Map完成了PB级数据的排序(PetaSort),也就是说,66000个Map处理了1PB数据(每个Map负责12.5GB)。但太多的Map在很短的时间内同时运行很容易造成反效果。
网格模式:除非应用程序的Map有严重的CPU限制,单个应用程序几乎没有任何理由需要超过60000-7000个Map。同样,当Map处理更大的数据块时,重要的是确保它们有足够的内存,以便排序缓冲区加速Map端排序(请阅读参考文档的io.sort.mb和io.sort.record.percent小节),如果Map输出可以直接在Map的排序缓冲区中处理,应用程序的性能可以大大提高,Map JVM必须承担更大的堆大小,重要的是要记住内存中去除序列化的输入大小和在磁盘上的大小可能有很大的不同,在这种情况下,应用程序需要更大的堆大小确保Map输入记录和Map输出记录可以保持在内存中。
网格模式:确保Map大小合适,以便所有Map输出可以保持在排序缓冲区中。
Map数量合适对应用程序有以下这些好处:
◆减少调度开销,更少的Map意味着任务调度也更简单,集群的可用性也更高;
◆Map端更高效,因为有足够的内存容纳Map输出;
◆减少了从Map向Reduce清洗Map输出需要的查找次数,记住每个Map为每个Reduce产生输出,因此查找次数等m*r,m表示Map数量,r表示Reduce数量。
◆每个清洗的片段更大,减少了建立连接的开销;
◆Reduce端合并了排序后的Map输出,效率更高,因为需要合并的Map输出片段更少了。
值得注意的是,每个Map处理太多的数据可能并不完全是好事,至少对故障恢复来说会很麻烦,即使是单点Map故障,也会造成严重的应用程序延迟。
网格模式:应用程序应使用较少的Map并行处理数据,确保不会出现糟糕的故障恢复情况。
合并器(Combiner)
合理使用合并器,应用程序可以获得更好的聚合效果,合并器最大的优势在于可以大大减少从Map到Reduce清洗的数据量。
清洗(Shuffle)
虽然使用合并器会得到更好的聚合效果,但它存在性能问题,因为它需要承担起额外的Map输出记录序列化/反序列化任务,应用程序可以使用合并器输入/输出记录计数器测量合并器的效率。
网格模式:合并器可以帮助应用程序减少清洗阶段的网络流量,但最重要的是要确保合并器要提供足够的聚合能力。
Reduce(化简)
Reduce的效率很大程度上是由清洗的性能决定的,应用程序配置的Reduce数量也很关键,太多或过少的Reduce都会产生反效果。
◆太少的Reduce会给节点造成负载过重,我曾看到最极端的情况,每个Reduce负责处理超过100GB的数据,同样,也会使故障恢复变得很困难,因为即便是单个Reduce故障也会引起显著的作业延迟。
◆太多的Reduce会给清洗闩带来不利影响,同样,在极端情况下,它会创建太多的小文件作为作业的输出,这会影响到应用程序以后处理小文件性能。
网格模式:应用程序应该确保每个Reduce最少可以处理1-2GB数据,最多5-10GB数据。
输出
一个关键因素是要记住应用程序的输出数量是和配置的Reduce数量呈线性关系的,正如前面提到的,配置数量适当的Reduce是非常重要的。此外,还需要考虑一些其它因素:
◆使用压缩程序对应用程序的输出做适当的压缩,提高HDFS写入性能;
◆每个Reduce不止输出一个输出文件,可以避免使用侧文件(side-file),应用程序通常会写一些侧文件来捕捉统计数据,如果所收集的统计数据很小,计数器可能更合适;
◆为Reduce输出使用合适的文件格式,对下游用户来说,使用zlib/gzip/lzo等编码器输出大量的文本压缩数据会适得其反,因为这些格式的文件无法再拆分,Map-Reduce框架必须强制单个Map处理整个文件,这会使负载均衡变得非常糟糕,并导致故障恢复变得很困难。应该使用SequenceFile和TFile格式缓解这些问题,因为它们既是可压缩的,又是可以再拆分的。
◆当独立输出文件很大时(数GB),最好使用更大的输出块大小(dfs.block.size)。
网格模式:应用程序输出少量的大文件,每个文件横跨多个HDFS块,并经过适当的压缩。
分布式缓存(DistributedCache)
分布式缓存高效分发应用程序相关的大型只读文件,它是Map-Reduce框架为应用程序缓存文件(文本,压缩文件,jar等)提供的一种手段,任何任务在从属节点上执行之前,Map-Reduce框架将会把必要的文件拷贝到从属节点上,其高效源于这些文件只会被复制一次,并提供从属节点上未压缩文件的缓存能力,它可以在Map或Reduce任务中作为一个最基本的软件分发机制,用于分发jar和本地库文件,只需要设定classpath或本地库路径即可。
分布式缓存被设计为主要用于分发少量中等规模的文件,大小从几MB到几十MB,分布式缓存当前实现的一个缺点是无法指定Map或Reduce的相关的产物(文件)。
在极少数情况下,由任务本身复制这些产物可能更恰当,例如,如果应用程序只配有少量Reduce,但需要分布式缓存中非常大型的产物(如大于512MB)。
网格模式:应用程序应该确保分布式缓存中的产物不能要求过多的I/O,不能多于应用程序任务真实的输入。
计数器(Counters)
这里指的是全局计数器,由Map/Reduce框架或应用程序定义,应用程序可以定义任意的计数器,然后在Map和/或Reduce方法中更新,这些计数器再通过框架进行全局汇总。
计数器应以跟踪少量的,重要的全局信息为妥,它们绝不是为了聚合非常细粒度的应用程序统计数据。
计数器代价非常高,因为JobTracker必须在整个应用程序生命周期维护每个Map/Reduce任务的计数器。
网格模式:应用程序不应该使用超过10,15或25个自定义计数器。
压缩
Hadoop Map-Reduce为应用程序中间Map输出和应用程序输出结果提供压缩,也就是说可以减少输出结果大小。
中间输出压缩:正如前面讲到的,采用适当的压缩编码对中间Map输出结果进行压缩,可以减少Map和Reduce之间的网络流量,从而提高性能,Lzo是压缩Map输出结果的理想选择,因为它在高CPU效率下提供了很好的压缩比。
应用程序输出压缩:采用适当的压缩编码和文件格式对应用程序输出结果进行压缩,可以提供更好的应用程序延迟,在大多数情况下,Zlib/Gzip可能是较好的选择,因为它们在合理的速度下提供了高压缩率,bzip2通常用于对压缩速度要求不要的情景。
全序输出(抽样)
有时应用程序需要产生全序输出,也就是说输出结果要全部排好序,在这种情况下,应用程序常用的一个反模式是使用单个Reduce,强制单一的全局聚合,很明显,这样做是非常低效的,不仅使Reduce任务所在的单个节点上的负载很重,也使故障恢复变得很困难。
更好的办法是对输入抽样,用抽样结果驱动采样分区程序,而不是默认的散列分区程序,这样才可以提供更好的负载均衡和故障恢复能力。
连接全序数据集
在网格上需要注意的另一个要素是连接两个全序数据集,注意,它们和基数可能不是精确的倍数关系,例如,一个数据集有512个 Bucket,而其它数据集只有200个Bucket。
在这种情况下,确保输入数据是全序的,这样应用程序就可以使用数据集的基数,Pig以高效的方式处理这些连接。
HDFS操作&JobTracker操作
NameNode是一个宝贵的资源,在网格中执行HDFS操作时,应用程序需要谨慎,特别是,我们不鼓励应用程序做非I/O操作,即Map/Reduce任务中的元数据操作,如递归统计,统计大型目录等。
同样,应用程序不应该为集群统计从后端联系JobTracker。
网格模式:应用程序不应该从后端在文件系统上执行任何元数据操作,他们应限制到作业提交期间的作业客户端,此外,应用程序不应该从后端联系JobTracker。
用户日志
用户任务日志,即Map/Reduce任务的srdout和stderr,存储在任务执行所在计算节点的本地磁盘上。
由于节点是共享基础设施的一部分,Map/Reduce框架限制了存储在节点上的任务日志数量。
Web用户界面
Hadoop Map/Reduce框架提供了一个基本的Web用户界面通过JobTracker跟踪运行中的作业,它们的进度和已完成作业历史等。
最重要的是要记住Web用户界面是提供给人使用的,而不是为自动化过程提供的。
实现自动化过程抓取Web用户界面是被严格禁止的,Web用户界面中的某些部件,如浏览作业历史,在JobTracker上是非常耗资源的,可能会导致严重的性能问题。
如果确实需要自动统计收集数据,最好咨询网格解决方案提供商,网格SE,或Map-Reduce开发团队。
工作流
Oozie是网格首选的工作流管理和调度系统,它可以基于时间或数据可用性管理工作流和提供调度方案,渐渐地,延迟敏感的生产作业管线也通过Oozie进行管理和调度。
设计Oozie工作流时需要牢记的一点是,Hadoop更适合批处理超大型数据,同样,从处理角度来看,工作流最好是由少量中到大型Map-Reduce作业组成,而不是由大量的小型Map-Reduce作业组成,作为一个极端的例子,我们曾看到过一个工作流由数千个作业组成的情景,这是一个很明显的反模式,就目前而言,Hadoop框架并不真正适合这种性质的业务,最好是将这些数以千计的Map-Reduce作业减少到合适的数量,这将有助于提高工作流性能,减少延迟。
网格模式:工作流中的单个Map-Reduce作业至少应该处理几十GB数据。
反模式
这一部分介绍一些在网格上运行的应用程序常见的反模式,通常它们不符合大规模,分布式,批量数据处理系统的精神。应用程序开发人员需要引起注意,因为网格软件堆栈正变得硬化,特别是即将发布的20.Fred,一些常见的反模式如下:
◆应用程序不使用如Pig等高级接口,除非确有必要。
◆处理成千上万的小文件(大小小于1 HDFS块,通常是128MB),使用一个Map处理单个小文件。
◆使用小的HDFS块大小(即128MB)处理非常大的数据集,导致需要数以万计的Map。
◆有大量Map(数千)的应用程序运行时很短(如5s)。
◆不使用合并器进行直接聚合。
◆应用程序Map数大于60000-70000个。
◆应用程序用很少的Reduce(如1个)处理大型数据集。
◆Pig脚本未用PARALLEL关键字处理大型数据集。
◆应用程序使用单个Reduce为输出记录实现全排序。
◆应用程序使用大量的Reduce处理数据,每个Reduce处理不到1-2GB数据。
◆应用程序为每个Reduce输出多个小型输出文件。
◆应用程序使用分布式缓存分发大量产物和/或非常大的产物(每一个数千MB)。
◆应用程序为每个任务使用数十个或数千个计数器。
◆应用程序从Map/Reduce任务在文件系统上执行元数据操作(如listStatus)。
◆应用程序为队列/作业的状态抓取JobTracker Web用户界面,或更糟的是已完成作业的历史。
◆工作流由数千个处理少量数据的小型作业组成。