本文另一地址请见Apache Hadoop 的最佳实践和反模式
本文译自 Apache Hadoop: Best Practices and Anti-Patterns
@AlfredCheung 同学亦对此文有贡献
Apache Hadoop是一个用来构建大规模共享存储和计算设施的软件。Hadoop集群已经应用在多种研究和开发项目中,并且,Yahoo!, EBay, Facebook, LinkedIn, Twitter等公司,越来越多的的把它应用在生产环境中。 这些已有的经验是技术和投入的结晶,在许多情况下至关重要。因此,适当的使用Hadoop集群可以保证我们的投入能够获得最佳回报。
这篇博文简单总结了一些Hadoop应用的最佳实践。实际上,类似于设计模式,我们引进一个网格模式的的概念,来提供一个通用且可复用的针对运行在网格上的应用的解决方案。
这篇博文列举了表现良好的应用的特点并且提供了正确使用Hadoop框架的各种特性和功能的指导。这些特点很大程度上这由其本身特点而定,阅读这篇文档的一个好方法是从本质上理解应用,这些最佳实践在Hadoop的多租户环境下卓有成效,而且不会与框架本身的大多数原则和限制产生矛盾。
博文还强调了一些Hadoop应用的反模式。
Hadoop上的数据处理应用一般使用Map-Reduce模型。
一个Map-Reduce作业通常会把输入的数据集拆分成许多独立的数据段,按照完全并行的方式一个map任务处理一段。框架把map的输出排序,然后作为reduce的输入。通常输入和输出都储存在文件系统。框架负责调度、监控任务的执行以及重启失败的任务。
Map-Reduce应用可以指定输入输出的位置,并提供了map与reduce功能的实现,体现在Hadoop中是Mapper 和Reducer.这些只是作业配置的一部分参数。Hadoop客户端提交作业(jar或者其他可执行的程序)和配置给JobTracker,而JobTracker负责把程序和配置分发到各个slave,调度和监控任务的执行,并返回状态信息给客户端。
Map/Reduce框架的处理是基于<key, value>这样的键值对,也就是说,框架吧输入数据视作一系列<key, value>键值对集合,然后产出另一些键值对作为输出。
这是 Map-Reduce应用的典型数据流
绝大多数在网格上运行的Map-Reduce应用都不会直接实现较低层次的Map-Reduce接口,而是借助于较高抽象层次的语言,例如Pig。
Oozie 是一个非常好的网格上的工作流管理和调度方案。Oozie 支持多种应用接口 (Hadoop Map-Reduce, Pig, Hadoop Streaming, Hadoop Pipes, 等等.) 并且支持基于时间或数据可用性的调度。
这一部分是关于网格上运行的Map-Reduce应用的最佳实践
Hadoop Map-Reduce 为处理海量数据而设计。maps过程以一种高度并行的方式来处理数据, 通常一个map至少处理一个HDFS block,一般是128M。
网格模式: 合并小文件以减少map数量,在处理大数据集的时候用比较大的HDFS 块大小。
maps的数量通常取决于输入大小, 也即输入文件的block数。 因此,假如你的输入数据有10TB,而block大小为128M,则需要82,000个map。
因为启动任务也需要时间,所以在一个较大的作业中,最好每个map任务的执行时间不要少于1分钟。
就像在上面“输入”部分所解释的,对于那种有大量小文件输入的的作业来说,一个map处理多个文件会更有效率。
如果应用处理的输入文件尺寸较大,每个map处理一个完整的HDFS block,数据段大一点更有效率。举个例子,让每个map处理更多数据,方法之一是让输入文件有更大的HDFS block尺寸,例如512M或者更多。
一个极端的例子是Map-Reduce开发团队用了大约66000个map来做PetaSort,也即66000个map要处理1PB数据,平均每个map 12.5G。
原则是大量运行时间很短的map会有损生产力。
网格模式:除非应用的map过程是CPU密集型,否则一个应用不应该有60000-70000个map。
当在map处理的block比较大的时候,确保有足够的内存作为排序缓冲区是非常重要的,这可以加速map端的排序过程。假如大多数的map输出都能在排序缓冲区中处理的话应用的性能会有极大的提升。这需要运行map过程的JVM具有更大的堆。记住反序列化输入的内存操作不同于磁盘操作;例如,Pig应用中的某些class将硬盘上的数据载入内存之后占用的空间会是其本来尺寸的3、4倍。在这种情况下,应用需要更大的JVM堆来让map的输入和输出数据能够保留在内存中。
网格模式:确保map的大小,使得所有的map输出可以在排序缓冲区中通过一次排序来完成操作。
合适的map数量有以下好处:
上述指南需要注意,一个map处理太多的数据不利于失败转移,因为单个map失败可能会造成应用的延迟。
适当的使用Combiner可以优化map端的聚合。Combiner最主要的好处在于减少了shuffle过程从map端到reduce端的传输数据量。
适当的使用Combiner可以优化map端的聚合。Combiner最主要的好处在于减少了shuffle过程从map端到reduce端的传输数据量。
Combiner 也有一个性能损失点,因为它需要一次额外的对于map输出的序列化/反序列化过程。不能通过聚合将map端的输出减少到20-30%的话就不适用combiner。可以用 combiner input/output records counters(译者注:这是一个hadoop mapreduce 的counter名称,所以采用了原名未翻译)来衡量Combiner的效率。
网格模式:Combiners可以减少shuffle阶段的网络流量。但是,要保证Combiner 的聚合是确实有效的。
reduces的性能很大程度上受shuffle的性能所影响。
应用配置的reduces数量是一个决定性的因素。
太多或者太少的reduce都不利于发挥最佳性能:
网格模式:在大多数情况下,应用应该保证每个reduce处理1-2g数据,最多5-10g。
输出
我们需要记住一个重要的因素——应用的输出文件数取决于配置的reduce数。从我们上文中对reduce的讨论可知,reduce数的选择十分关键。
此外,还需要考虑其它一些因素:
网格模式: 应该确保应用的输出是数量不多的大文件,每个文件跨越多个HDFS块,而且经过适当的压缩。
分布式缓存可以高效的分发与具体应用相关的较大尺寸的只读文件。这是Map/Reduce框架提供的机制,用于暂时存储与特定应用相关的文件(如text, archives, jars等)。
这个框架会在slave节点上执行任务之前将必要的文件拷贝到该节点。它如此高效是因为在个作业中所需要的文件只会被复制一遍,还因为它能够缓存slave节点上的未归档文件。它也被作为基本软件分发机制用于map和reduce 任务。这种机制可以把jar和本地库放置在map/reduce任务的classpath或者本地库路径下。
分布式缓存设计之初是为了分发一些尺寸不是很大,从几M到几十M的附件。目前实现的分布式缓存的弱点在于不能够指定具体的附件只能应用于特定的map或者reduce。
在极少数情况下,由具体任务本身来拷贝其所需的附件要比使用分布式缓存更合适。例如,那种reduce数很少的应用,而且需要的附属文件尺寸非常大(超过512M)。
网格模式:应用应该保证分布式缓存中的附件不能够比任务本身的I/O消耗更多。
计数器(Counters) 展现一些全局性的统计度量,这些度量由mapreduce框架本身,也可由应用来设定。应用可以自行定义任意的计数器并且在map或者reduce方法中更新它们的值。框架会对计数器的值做全局聚合。
计数器适合于追踪记录一些量不是很大,但是很重要的全局性信息。不应该用于一些粒度过细的信息统计。
使用计数器的代价非常昂贵,因为在应用的生命周期内JobTracker 需要给每一个map/reduce任务维护一组计数器(定义了多少个就维护多少个)。
网格模式:应用自定义的计数器不应该超过25个。
Hadoop Map-Reduce 可以在应用中对map输出的中间数据和reduce的输出数据进行指定的压缩。
有时候,应用需要产生全排序的输出。在这种情况下,一个通用的反模式是只使用一个reduce,这样就能强制数据集中在一处做聚合。很明显,这样做效率不高,这样不仅加大了执行reduce的那个节点的负载,还对失败恢复有严重的不良影响。
更好的办法是对输入抽样,然后以此来使用sampling partitioner 代替默认的hash partitioner。这样可以获得更好的负载平衡和失败恢复。
另一种网格设计模式是关于两个有序数据集的连接,其中一个数据集的大小并非另一个的严格倍数。例如,一个数据集有512个buckets,另一个有200个。
在这种情况下,确保输入的数据集是整体有序的(全排序,如同在商议部分所提到的)意味着可以使用两个数据集中的任意一个来作为基数。Pig 就是用这种发发来进行高效的连接。
NameNode 很重要而且负担要比一般的节点重,所以在进行HDFS 操作的时候要注意对性能的影响。特别是,应用程序不要在map/reduce任务中做非I/O操作,也即像遍历目录,递归统计等这样的元数据操作。
同样,不要在应用程序中连接JobTracker来获得关于集群统计的数据。
网格模式:应用不应该在代码中执行任何文件系统的元数据操作,这种操作应该在作业提交的时候被严格禁止。除此以外,应用程序不应该自己连接JobTracker 。
与用户执行的任务相关的task-logs,也即 map/reduce 任务的标准输出和错误信息储存在执行这个任务的节点的本地磁盘上。
因为每个节点都是共享存储的一部分,所以Map-Reduce 框架对储存在节点上的log数量实际上是有限制的。
Hadoop Map-Reduce 框架提供了一个简单的web界面来监控运行中的作业,查看已完成作业的历史,以及其他一些从JobTracker获得的信息
要明白这个web界面是给人看的而不是自动程序。
通过一些屏幕自动化软件来从web界面获取信息是不可行的。web界面上的某些部分,像查看历史作业,非常消耗JobTracker 的资源,如果使用屏幕自动化软件这么做可能会导致一些性能问题。
假如真有这么一个自动统计汇总的需求,最好去咨询 Map-Reduce的开发团队。
Oozie 是一个适用于网格应用的非常好的工作流管理和调度系统。Oozie 可以基于时间或者数据可用性来管理和计划工作流。使用Oozie来管理和调度的低延迟要求的,产品级的项目已经越来越多。
设计Oozie 的时候考虑的一个重要因素是Hadoop 更适宜于批量处理大量数据。正因如此,用几个中等规模的Map-Reduce组成处理流程,要比用更多的小型的Map-Reduce作业更好。在极端情况下一个流程可能由几百上千个作业组成,这是很明显的反模式。更好的做法是能够将这些Map-Reduce作业重新组装成较少的几个过程,每个过程处理更多的数据,这有助于提高整个流程的性能并降低延迟。
网格模式:工作流中一个Map-Reduce作业应该至少处理十几G数据。
这部分会总结一些网格应用通用的反模式。这些东西大多数情况下都与大规模、分布式、批量数据处理系统的原则相悖。这是对应用开发人员的提醒,因为网格软件逐渐规范化固定化,特别是即将发布的20.Fred版本,对于具有下面列出的这些反模式的应用更难容忍。