Hadoop(四) -- MapReducer(二)maptask和reducetask并行度

本文详细探讨了Hadoop MapReduce中MapTask和ReduceTask的并行度。MapTask并行度由文件切片决定,通常与HDFS分块大小相同,默认为128M。小文件场景下可能导致过多的MapTask,可通过CombineFileInputFormat合并处理。ReduceTask并行度可自定义,默认使用Hash分区,可继承Partitioner实现业务逻辑分区。数据倾斜问题由不合理的分区规则引起,解决方案包括优化分区算法,避免数据不平衡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、MapTask的并行度

        运行map部分的任务叫做maptask,并行度是指同时运行的maptask的任务个数。maptask处理的数据量对应于一个文件切片,每个maptask处理一个文件切片大小的数据。

        MapReducer任务运行时通过 FileInputFormat 类传入输入数据文件,该类在读取文件时会调用 getSplit() 方法对文件进行逻辑切片。

        getSplit() 部分源码如下,当使用默认配置时切片大小默认等于HDFS分块大小128M

/** 
   * Generate the list of files and make them into FileSplits.
   * @param job the job context
   * @throws IOException
   */
  public List<InputSplit> getSplits(JobContext job) throws IOException {
    long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));  // 1
    long maxSize = getMaxSplitSize(job);  // LONG_MAX_VALUE
    
    // 块的切分标准,Hadoop2.x 默认128M
    long blockSize = file.getBlockSize();
    // 计算切片大小
    long splitSize = computeSplitSize(blockSize, minSize, maxSize);
  }

  // 返回三个值得中间值
  protected long computeSplitSize(long blockSize, long minSize, long maxSize) {
        return Math.max(minSize, Math.min(maxSize, blockSize));
  }

切片和分块的关系:

        默认情况下大小相同。切块是物理概念,针对于HDFS的数据存储。切片是逻辑概念,针对于maptask的并行度。

        由于HDFS存储时以块为单位,数据文件不足128M也会单独存储一个块,默认情况下maptask处理的数据量与一个块的数据量相同。对于多个小文件的情况,如共100个小文件,每个文件1KB。虽然所有文件一共才100KB,但默认要启动100maptask。此时可使用CombineFileInputFormat对小文件进行合并再做mapreduce处理。

二、ReduceTask的并行度

        reducetask设置为多个就是将原来的一个大任务分成多个小任务,每一个任务负责一部分数据。既然要将数据进行拆分,就要遵循一个拆分的规则,避免每个小任务只对部分数据可见。数据的拆分就是分区,每个reducetask对应处理一个分区的数据。

        若不指定分区规则,默认采用Hash分区,源码如下

public class HashPartitioner<K, V> extends Partitioner<K, V> {

  /** Use {@link Object#hashCode()} to partition. */
  /**
  * K key : map输出的key
  * V Value : map输出的value
  * int numReduceTasks : 用户指定的ReduceTask任务个数
  public int getPartition(K key, V value, int numReduceTasks) {
        return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
                // 避免hashCode取值超过Integer.MAX_VALUE,求与后超出int部分全为0
  }

}

自定义分区:按照业务逻辑定义分区规则。

        1. 继承Partitioner

        2. 重写getPartition()方法

    /**
    * KEY : map输出key的类型
    * VALUE : map输出value的类型
    /
	class MyPartition extends Partitioner<KEY, VALUE> {

		public int getPartition(Text s, IntWritable integer, int numPartitions) {
            s = s.toString();
			if (s.startsWith("a")) return 0;
			else if (s.startsWith("b")) return 1;
            else return 2;
		}
	}

        Driver类中指定分区类和分区个数。如不指定分区个数,默认按照一个分区处理。若指定分区个数小于自定义分区个数报错,大于自定义分区个数,未指定分区生成空的结果文件。

job.setPartitionerClass(MyPartition.class);
job.setNumReduceTask(3);

说明:一个节点可以运行多个maptask和reducetask任务,但一个maptask或reducetask任务只能在一个节点上运行。

        由于两种任务对于数据分配的规则不同,maptask采用分片对数据进行切分,每个部分的数据大小都是相同的。而reducetask处理的数据量大小与分区规则有关,若分区规则设置不合理,造成某个分区数据量过大,将会造成数据倾斜问题,即一个reducetask要处理大量数据,而其它reducetask只处理少部分数据。

        解决办法:合理设计分区算法。针对业务数据可采用字符串反转、加盐、加时间戳等方式避免数据倾斜。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值