MapReduce优化_1

**

1、配置调优

**
调优总的原则给shuffle过程尽量多提供内存空间,在map端,可以通过避免多次溢出写磁盘来获得最佳性能(相关配置io.sort.*,io.sort.mb),在reduce端,中间数据全部驻留在内存时,就能获得最佳性能,但是默认情况下,这是不可能发生的,因为一般情况所有内存都预留给reduce含函数(如需修改 需要配置mapred.inmem.merge.threshold,mapred.job.reduce.input.buffer.percent)

如果能够根据情况对shuffle过程进行调优,对于提供MapReduce性能很有帮助。
一个通用的原则是给shuffle过程分配尽可能大的内存,当然你需要确保map和reduce有足够的内存来运行业务逻辑。因此在实现Mapper和Reducer时,应该尽量减少内存的使用,例如避免在Map中不断地叠加。
运行map和reduce任务的JVM,内存通过mapred.child.java.opts属性来设置,尽可能设大内存。容器的内存大小通过mapreduce.map.memory.mb和mapreduce.reduce.memory.mb来设置,默认都是1024M。

可以通过以下方法提高排序和缓存写入磁盘的效率:

调整mapreduce.task.io.sort.mb大小,从而避免或减少缓存溢出的数量。当调整这个参数时,最好同时检测Map任务的JVM的堆大小,并必要的时候增加堆空间。
mapreduce.task.io.sort.factor属性的值提高100倍左右,这可以使合并处理更快,并减少磁盘的访问。
为K-V提供一个更高效的自定义序列化工具,序列化后的数据占用空间越少,缓存使用率就越高。
提供更高效的Combiner(合并器),使Map任务的输出结果聚合效率更高。
提供更高效的键比较器和值的分组比较器。
输出依赖于作业中Reduce任务的数量,下面是一些优化建议:

压缩输出,以节省存储空间,同时也提升HDFS写入吞吐量
避免写入带外端文件(out-of-band side file)作为Reduce任务的输出
根据作业输出文件的消费者的需求,可以分割的压缩技术或许适合
以较大块容量设置,写入较大的HDFS文件,有助于减少Map任务数
**

2、Map/Reduce端调优

**
通用优化
Hadoop默认使用4KB作为缓冲,这个算是很小的,可以通过io.file.buffer.size来调高缓冲池大小。

map端优化
避免写入多个spill文件可能达到最好的性能,一个spill文件是最好的。通过估计map的输出大小,设置合理的mapreduce.task.io.sort.*属性,使得spill文件数量最小。例如尽可能调大mapreduce.task.io.sort.mb。
在这里插入图片描述
reduce端优化
如果能够让所有数据都保存在内存中,可以达到最佳的性能。通常情况下,内存都保留给reduce函数,但是如果reduce函数对内存需求不是很高,将mapreduce.reduce.merge.inmem.threshold(触发合并的map输出文件数)设为0,mapreduce.reduce.input.buffer.percent(用于保存map输出文件的堆内存比例)设为1.0,可以达到很好的性能提升。在TB级别数据排序性能测试中,Hadoop就是通过将reduce的中间数据都保存在内存中胜利的。
在这里插入图片描述
在这里插入图片描述

**

3、Combiner与Partitioner

**
Combiner和Partitioner是用来优化MapReduce的。可以提高MapReduce的运行效率。

Combiner

集群上的可用带宽限制了MapReduce作业的数量,因此尽量避免map和reduce任务之间的数据传输是有利的。Hadoop允许用户针对map任务的输出指定一个combiner(就像mapper,reducer)。combiner函数的输出作为reduce函数的输入。由于combiner术语优化方案,所以Hadoop无法确定对map任务输出记录调用多少次combiner(如果需要)。换言之,不管调用多次combiner,reducer的输出结果都是一样的。

首先通过下面的示意图直观的了解一下Combiner的位置和作用。
从下图可以看出,Combiner介于 Mapper和Reducer之间,combine作为 Map任务的一部分,执行完 map 函数后紧接着执行combine,而reduce 必须在所有的 Map 任务完成后才能进行。 而且还可以看出combine的过程与reduce的过程类似,都是对相同的单词key合并其词频,很多情况下可以直接使用reduce函数来完成Combiner过程。
在这里插入图片描述
Combiner解析:

  1. ombiner可以看做局部的Reducer(local reducer)。
  2. Combiner作用是合并相同的key对应的value。
  3. 在Mapper阶段,不管Combiner被调用多少次,都不应改变 Reduce的输出结果。
  4. Combiner通常与Reducer的逻辑是一样的,一般情况下不需要单独编写Combiner,直接使用Reducer的实现就可以了。
  5. Combiner在Job中是如下设置的。 job.setCombinerClass(Reducer.class);//Combiner一般情况下,默认使用Reducer的实现

Combiner的优点

能够减少Map Task输出的数据量(即磁盘IO)。对spill,merge文件都可以进行压缩。
中间结果非常大导致IO成为瓶颈时压缩非常有用,可以通过mapreduce.map.output.compress(default:false)设置为true进行压缩,数据会被压缩写入磁盘,读数据读的是压缩数据需要解压,在实际经验中Hive在Hadoop的运行的瓶颈一般都是IO而不是CPU,压缩一般可以10倍的减少IO操作,压缩的方式Gzip,Lzo,BZip2,Lzma等,其中Lzo是一种比较平衡选择,mapreduce.map.output.compress.codec(default:org.apache.hadoop.io.compress.DefaultCodec)参数设置。但这个过程会消耗CPU,适合IO瓶颈比较大。
能够减少Reduce-Map网络传输的数据量(网络IO)。Map Task 输出越少,Reduce从Map结果中拉取的数据量就越少,自然就减少了网络传输的数据量。
Combiner的使用场景

并不是所有的场景都可以使用Combiner,必须满足结果可以累加。
适合于Sum()求和,并不适合Average()求平均数。
例如,求0、20、10、25和15的平均数,直接使用Reduce求平均数Average(0,20,10,25,15),得到的结果是14, 如果先使用Combiner分别对不同Mapper结果求平均数,Average(0,20,10)=10,Average(25,15)=20,再使用Reducer求平均数Average(10,20),得到的结果为15,很明显求平均数并不适合使用Combiner。
**

Partitioner

**
Partitioner 处于 Mapper阶段,当Mapper处理好数据后,这些数据需要经过Partitioner进行分区,来选择不同的Reducer处理,从而将Mapper的输出结果均匀的分布在Reducer上面执行。

对于map输出的每一个键值对,系统都会给定一个partition,partition值默认通过计算key的hash值后对Reduce task的数量取模获得。如果一个键值对的partition值为1,意味着这个键值对会交给第一个Reducer处理。
在这里插入图片描述

Partitioner解析:

Partitioner决定了Map Task 输出的每条数据交给哪个Reduce Task 来处理。Partitioner 有两个功能:
1) 均衡负载。它尽量将工作均匀地分配给不同的 Reduce。
2)效率。它的分配速度一定要非常快。

Partitioner 的默认实现:hash(key) mod R,这里的R代表Reduce Task 的数目,意思就是对key进行hash处理然后取模。很多情况下,用户需要自定义 Partitioner,比如“hash(hostname(URL)) mod R”,它确保相同域名下的网页交给同一个 Reduce Task 来处理。 用户自定义Partitioner,需要继承Partitioner类,实现它提供的一个方法。

public class WordCountPartitioner extends Partitioner<Text, IntWritable> {
    @Override
    public int getPartition(Text key, IntWritable value, int numPartitions) {
        // TODO Auto-generated method stub
        int a = key.hashCode()%numPartitions;
        if(a>=0)
            return a;
        else 
            return 0;
    }
}

**

自定义partitioner

**

每一个Reduce的输出都是有序的,但是将所有Reduce的输出合并到一起却并非是全局有序的,如果要做到全局有序,我们该怎么做呢?最简单的方式,只设置一个Reduce task,但是这样完全发挥不出集群的优势,而且能应对的数据量也很受限。最佳的方式是自己定义一个Partitioner,用输入数据的最大值除以系统Reduce task数量的商作为分割边界,也就是说分割数据的边界为此商的1倍、2倍至numPartitions-1倍,这样就能保证执行partition后的数据是整体有序的。

解决数据倾斜:另一种需要我们自己定义一个Partitioner的情况是各个Reduce task处理的键值对数量极不平衡。对于某些数据集,由于很多不同的key的hash值都一样,导致这些键值对都被分给同一个Reducer处理,而其他的Reducer处理的键值对很少,从而拖延整个任务的进度。当然,编写自己的Partitioner必须要保证具有相同key值的键值对分发到同一个Reducer。

自定义的Key包含了好几个字段,比如自定义key是一个对象,包括type1,type2,type3,只需要根据type1去分发数据,其他字段用作二次排序。

猜你喜欢

转载自blog.csdn.net/suansn/article/details/84330170
今日推荐