如何提高Map-Reduce性能

时间:2020-01-09 10:34:35  来源:igfitidea点击:

在本文中,我们将介绍提高Hadoop中的Map-Reduce作业性能的一些方法。

此处给出的改善MapReduce作业性能的技巧更多地是从MapReduce代码和配置的角度出发,而不是从集群和硬件的角度出发。

1启用uber模式–与Hadoop 1一样,YARN Hadoop中没有JVM重用功能,但是我们可以启用任务以Uber模式运行,默认情况下未启用uber。如果启用了超级模式,ApplicationMaster可以计算出与ResourceManager进行资源协商,与不同节点上的NodeManagers进行通信以启动容器并在这些容器上运行任务的开销远大于在同一JVM中按顺序运行MapReduce作业的开销。一项艰巨的任务。

2对于压缩,请尝试使用本机库–在Hadoop中使用压缩和解压缩时,最好使用本机库,因为本机库将优于以Java之类的编程语言编写的编解码器。

3增加块大小–如果输入文件的大小非常大,则可以考虑将hdfs块大小提高到512M。这可以通过设置参数dfs.blocksize来实现。如果将dfs.blocksize设置为较高的值,则输入分割大小将增加为相同大小,因为输入大小是使用公式计算的。

Math.max(mapreduce.input.fileinputformat.split.minsize,Math.min(mapreduce.input.fileinputformat.split.maxsize,dfs.blocksize))

因此使其大小与HDFS块大小相同。通过增加块大小,我们将在元数据方面减少开销,因为块数量将减少。
如果输入拆分较大,则Map任务将获取更多数据进行处理。在Hadoop中,随着输入拆分的开始,启动了许多映射任务,因此,输入拆分较少的情况意味着初始化映射任务的开销减少了。

4地图任务花费的时间–如果地图任务完成的时间少于一分钟,则该地图任务应至少运行一分钟(1-3分钟),这意味着向地图任务输入的数据较少。如果地图缩小作业中有许多小文件,请尝试使用包含这些小文件的容器文件格式,例如序列文件或者Avro。
我们还可以使用CombineFileInputFormat,它将许多文件放入一个输入拆分中,以便有更多数据供映射器处理。

5输入数据压缩是否可拆分–如果输入数据已压缩,则使用的压缩格式是否可拆分也是要考虑的事情之一。如果输入数据不可拆分,则单个映射任务将仅处理单个拆分,这会使处理速度非常慢,并且根本没有并行性。

为了压缩输入数据,请使用可拆分的bzip2或者使用带索引的lzo使其可拆分来进行压缩。

6设置化简任务的数量–映射数通常由输入分割数驱动,但化简器的数量可以控制。根据文档;减少的正确数量似乎是0.95或者1.75乘以(<节点数> * <每个节点的最大容器数>)。
有了0.95,所有的reduce都可以立即启动,并在地图结束时开始传输地图输出。速度为1.75时,更快的节点将完成其第一轮还原,并发起第二次还原,从而更好地完成了负载平衡。

增加减少的数量会增加框架开销,但会增加负载平衡并降低故障成本。

用户通过Job.setNumReduceTasks(int)设置作业的减少数量。

7减速器端的数据偏斜–如果数据偏斜,使得更多的值与单个键组合在一起而不是值的均匀分布,则减少处理具有更多值的键的任务将需要更多的时间来完成其他减速器由于分布不均,将获得较少的数据,并且提早完成。
在这种情况下,请尝试分析数据的分区并查看编写自定义分区程序的可能性,以便将数据均匀地分布在各个键之间。

8Shuffle阶段性能改进– Hadoop框架中的Shuffle阶段非常耗费网络资源,因为文件是从映射器传输到reducer的。当将映射输出写入本地磁盘时,会涉及很多IO,还有许多处理工作,如按照reducer对数据进行分区,按键对数据进行排序,合并等形式。
为减少混洗阶段时间而进行的优化有助于减少总的工作时间。一些性能改进技巧如下:

  • 压缩映射输出–由于将映射输出写入磁盘并也传输到reducer,因此压缩映射输出可节省存储空间,使其更快地写入磁盘并减少必须传输到reducer节点的数据。
  • 过滤数据–了解如何减少Map任务发出的数据。过滤记录以完全删除不需要的记录。另外,通过仅采用相关记录字段来减小记录大小。
  • 使用组合器–在MapReduce中使用组合器是提高整体MapReduce作业性能的好方法。通过使用合并器,我们可以在映射阶段本身中聚合数据,并减少发送到缩减器的记录数。
  • 原始比较器–在排序和合并期间,Hadoop框架使用比较器比较键。如果我们使用的是自定义比较器,则尝试将其编写为原始比较器,以便可以在字节级别本身进行比较。否则,将反序列化地图任务中的键以创建对象,然后进行比较,从而使过程很耗时。
  • 使用最佳值设置参数–我们可以采取的另一项提高MapReduce作业性能的措施是更改某些配置参数的值。目标是减少在map上溢出到磁盘的记录以及减少方面。在地图端,我们可以更改以下参数的设置,以尝试减少溢出到磁盘的次数。 mapreduce.task.io.sort.mb –排序文件时要使用的缓冲内存总量,以兆字节为单位。 mapreduce.map.sort.spill.percent –序列化缓冲区中的软限制。一旦到达,线程将开始将内容溢出到后台的磁盘中。在reduce端,我们可以更改以下参数的设置,以尝试将数据保留在内存中。随机播放期间从最大堆大小分配到存储映射输出的内存百分比。 mapreduce.reduce.input.buffer.percent –相对于最大堆大小的内存百分比,用于在缩减期间保留映射输出。 mapreduce.reduce.shuffle.memory.limit.percent –单个shuffle可以消耗的内存限制的最大百分比。

9,MapReduce编码方面的改进–我们还应该优化MapReduce代码,使其高效运行。

  • 重用对象–由于多次调用map方法,因此明智地创建新对象将有助于我们减少与对象创建相关的开销。尽量重用对象。经常发生的错误之一是如下编写代码。
String[] stringArr = value.toString().split("\s+");
Text value = new Text(stringArr[0]);
context.write(key, value);

我们应该将其写为以下内容

private Text value = new Text();
public void map(LongWritable key, Text value, Context context) 
    throws IOException, InterruptedException {
  String[] stringArr = value.toString().split("\s+");
  value.set(stringArr[0]);// reusing object
  context.write(key, value);
}
  • 字符串连接–由于Java中的字符串是不可变的,因此字符串连接会导致创建字符串对象。对于追加,则首选StringBuffer或者StringBuilder。