天天看点

《Hadoop实战第2版》——3.3节MapReduce任务的优化

3.3 mapreduce任务的优化

相信每个程序员在编程时都会问自己两个问题“我如何完成这个任务”,以及“怎么能让程序运行得更快”。同样,mapreduce计算模型的多次优化也是为了更好地解答这两个问题。

mapreduce计算模型的优化涉及了方方面面的内容,但是主要集中在两个方面:一是计算性能方面的优化;二是i/o操作方面的优化。这其中,又包含六个方面的内容。

任务调度

任务调度是hadoop中非常重要的一环,这个优化又涉及两个方面的内容。计算方面:hadoop总会优先将任务分配给空闲的机器,使所有的任务能公平地分享系统资源。i/o方面:hadoop会尽量将map任务分配给inputsplit所在的机器,以减少网络i/o的消耗。

数据预处理与inputsplit的大小

mapreduce任务擅长处理少量的大数据,而在处理大量的小数据时,mapreduce的性能就会逊色很多。因此在提交mapreduce任务前可以先对数据进行一次预处理,将数据合并以提高mapreduce任务的执行效率,这个办法往往很有效。如果这还不行,可以参考map任务的运行时间,当一个map任务只需要运行几秒就可以结束时,就需要考虑是否应该给它分配更多的数据。通常而言,一个map任务的运行时间在一分钟左右比较合适,可以通过设置map的输入数据大小来调节map的运行时间。在fileinputformat中(除了combinefileinputformat),hadoop会在处理每个block后将其作为一个inputsplit,因此合理地设置block块大小是很重要的调节方式。除此之外,也可以通过合理地设置map任务的数量来调节map任务的数据输入。

map和reduce任务的数量

合理地设置map任务与reduce任务的数量对提高mapreduce任务的效率是非常重要的。默认的设置往往不能很好地体现出mapreduce任务的需求,不过,设置它们的数量也要有一定的实践经验。

首先要定义两个概念—map/reduce任务槽。map/reduce任务槽就是这个集群能够同时运行的map/reduce任务的最大数量。比如,在一个具有1200台机器的集群中,设置每台机器最多可以同时运行10个map任务,5个reduce任务。那么这个集群的map任务槽就是12000,reduce任务槽是6000。任务槽可以帮助对任务调度进行设置。

设置mapreduce任务的map数量主要参考的是map的运行时间,设置reduce任务的数量就只需要参考任务槽的设置即可。一般来说,reduce任务的数量应该是reduce任务槽的0.95倍或是1.75倍,这是基于不同的考虑来决定的。当reduce任务的数量是任务槽的0.95倍时,如果一个reduce任务失败,hadoop可以很快地找到一台空闲的机器重新执行这个任务。当reduce任务的数量是任务槽的1.75倍时,执行速度快的机器可以获得更多的reduce任务,因此可以使负载更加均衡,以提高任务的处理速度。

combine函数

combine函数是用于本地合并数据的函数。在有些情况下,map函数产生的中间数据会有很多是重复的,比如在一个简单的wordcount程序中,因为词频是接近与一个zipf分布的,每个map任务可能会产生成千上万个记录,若将这些记录一一传送给reduce任务是很耗时的。所以,mapreduce框架运行用户写的combine函数用于本地合并,这会大大减少网络i/o操作的消耗。此时就可以利用combine函数先计算出在这个block中单词the的个数。合理地设计combine函数会有效地减少网络传输的数据量,提高mapreduce的效率。

在mapreduce程序中使用combine很简单,只需在程序中添加如下内容:

压缩

编写mapreduce程序时,可以选择对map的输出和最终的输出结果进行压缩(同时可以选择压缩方式)。在一些情况下,map的中间输出可能会很大,对其进行压缩可以有效地减少网络上的数据传输量。对最终结果的压缩虽然会减少数据写hdfs的时间,但是也会对读取产生一定的影响,因此要根据实际情况来选择(第7章中提供了一个小实验来验证压缩的效果)。

自定义comparator

在hadoop中,可以自定义数据类型以实现更复杂的目的,比如,当读者想实现k-means算法(一个基础的聚类算法)时可以定义k个整数的集合。自定义hadoop数据类型时,推荐自定义comparator来实现数据的二进制比较,这样可以省去数据序列化和反序列化的时间,提高程序的运行效率(具体会在第7章中讲解)。