分类:
2010-09-09 13:34:38
对一 个job的map数和reduce数的设定对一个job的运行是非常重要的,并且非常简单。以下是一些这几个值的经验总结:
hadoop distcp -Ddfs.block.size=$[256*1024*1024] /path/to/inputdata /path/to/inputdata-with-largeblocks的方式来将已经存在咋hdfs上的数据进行大块化。然后删除掉原先的文件。
测试对比:
调 整运行参数( -Dmapred.max.split.size=$[16*1024*1024]
), 或者在配置文件中对将 mapred.max.split.size设置成$[16*1024*1024]
,hadoop 中的wordcount任务的mapper数就会受到用户控制。当运行这种配置的任务时,每个task都会在10秒钟之内运行完,而从 jobtracker的webui上可以看到cluster的总体情况和job的情况,其中可以看到,running的map数频繁的在0-24之间波 动。整个job17分52秒完成,比使用原始配置的job的运行时间的两倍还多。
2.当运行中发现如下的现象,通常job是 可以优化的:
如 果job的算法中涉及到许多的排序操作,可以尝试写一个来 提高job性能。的框架中提供了Combiner来减少中间结 果对磁盘的写入和减少中间结果在mapper和reducer之间的传输,通常这两个方面都是非常影响作业性能的两个方面。
性 能对比:
修改wordcount程序,将setCombinerClass去掉,或者不去掉,两种方式对比 运行。去掉的结果是,让每一个mapper的运行时间由原先的33s变成了平均48s,并且shuffle过程中的中间也由1G变成1.4GB,整个的job由原先的8分30 秒变成了15分42秒,将近两倍了。而且,这个测试还是在enable了map的output 压缩的情况下进行的,如果disable这个特性,性能的影响可能会更加大。
3.
如果在mapreduce的作业中有类似这样的情况,那么就肯定是可以优化的:
IntWritable
orLongWritable
objects are used when most output values tend to be significantly smaller than the maximum value.当mapreduce的新手写mapreduce程序时,或是习惯了 hadoop的streaming作业的使用方式,转而使用mapreduce来写程序时,常常会有人不正确的使用Text类,很多的时候,没有这个必 要。虽然使用Text非常方便,但是,从数字类型转换成UTF8的字符串的效率是很低的,如果这样的操作很多,是会占用过多的cpu时间而导致作业效率降 低。任何时候,当处理非文本数据(数字,浮点等类型),使用IntWritable或者FloatWritable的效率会高很多。
除了 Text的问题,二进制的对象通常还会占 用更少的存储。由于磁盘IO和的传输常常都是 mapreduce作业的瓶颈所在,这样做可以很大程度的提高job的性能,尤其是当job的规模很大的时候。当处理Integer数据时,同样有时是可 以使用VIntWritable或者VLongWritable来提高性能。不仅可以减少cpu占用,也可以减少空间占用。例如,4,当序列化时会被序列 化成1个byte,而10000序列化时会占用2个bytes。这种类似couters的值,序列化时的length就会很重要。当数据都是很小的数字, 而只有一些是很大的时候,这样的效率节约是非常可观的。
如果hadoop中自带的Writable的各种类型不能满足需要,可以自己写一 个。非常的简单,这样也可能会比直接用Text,然后解析来的高效。在写自己的Writable时,尝试提供一个RawComparator(可以从 hadoop的源码中找到类似的实现)。
同样,如果maprduce job是一系列job中的一个,可以在中间结果和中间job的output中都使用SequenceFile来保存中间结果,即使最后的输出需要使用文本 化的数据。这样可以减少数据的中间结果,减低磁盘的 IO,并且节省网络传输。
测试对比:
对 于example中的word count job,我们可以修改value中的数据类型从IntWritable为Text,在reduce中,用Integer.parseString(value.toString()) 来接收value信息。job的性能直接就降低了10%,整个job花了9分钟,并且每一个map都花费了36秒钟的时间,原先只需要33秒。由于 Integer的parse本身比较快,这样做并没有太大程度上的影响性能,而在通常情况下,不同的使用方式会产生2-3倍的性能差。
4.
hadoop的mapreduce作业通常有如下描述时,会不够高效,如下:
对于mapredcue的新手来说,常常会犯这样的一个错误,在自己的程序 中大量的new Writable对象。尤其是在mapper函数或者reducer函数中。例如,有些人可能会这样来实现wordcunt:
public void map(...) {
...
for (String word : words) {
output.collect(new Text(word), new IntWritable(1));
}
}
这种实现会造成:非常非常多的临时对象被分配内存。
这样会造成jvm花费很多的时间来进行垃圾回收。正确的做法是:
class MyMapper ... {
Text wordText = new Text();
IntWritable ne = new IntWritable(1);
public void map(...) {
...
for (String word : words) {
wordText.set(word);
output.collect(word, one);
}
}
}
这就是说,在mapreduce中,Writable对象是可重用的,其实这也是hadoop中为什么会使用
writable对象(如Text,IntWritable等),而不是采用原声的 对应对象的原因。
测 试对比:
当把wordcount的代码修改成以上的样子,job的运行时间并没有太大的改变,但是这是因为在配置中默认 设定了每个task的heap size的最大为1GB,所以GC在jvm的内存占用没有到一定阈值的时候根本就没有运行。但是,如果将heap size修改为200MB,那么原本8分30秒能完成的作业,变成了17分钟才能完成。所以,在hadoop的mapreduce代码书写的时候,注意这 样的一些小,是非常有用的。