7.MR核心_Mapper_Reducer

1.Mapper

在上一节我们看到了FileInputFormat把数据进行分片,然后提供RecordReader给Mapper。我们看看Mapper会如何处理:

/** 
 * Mappers是多个将input records转换为中间临时records的单个任务。
 * 转换后的中间records不需要与input recordsx类型相同。
 * 一个给定的<k,v>可能映射到0或多个<k,v>。
 * 
 * MR框架通过InputFormat为每个Mapper 任务生成一个InputSplit。Mapper通过JobContext.getConfiguration()访问Job的配置。
 * MR框架会先调用且仅调用一次setup(...)方法,紧接着为每个<k,v>执行map(...)方法,最后调用cleanup(...)方法。
 * 
 * 与一个指定Key相关的所有中间值Value紧接着将会被MR框架分成一组。传给一个Reducer取决定最后输出。
 * 用户可以通过分别指定两个RawComparator比较器来控制Key的排序和分组。 
 * 
 * Mappers的输出被分区到每个Reducer。用户能通过自定义一个Partitioner控制哪些Key被分配到哪个Partitioner中。
 * 
 *
 * 用户可以选择通过Job.setCombinerClass(...)指定一个"聚合输出器"实现本地聚合,有助于减少从Mapper到Reducer的数据传输量。
 * 
 * 用户可以指定应用是否以及如何压缩Mapper中间输出,以及使用哪些CompressionCodecs配置压缩类型。
 *  
 * 如果Job没有Reducer,那么Mapper的输出将直接写入OutputFormat,无需按Key排序。
 * 
 */
public class Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {

  // Context实现与Mappers上下文传递
  public abstract class Context
    implements MapContext<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {
  }
  
  /**
   * Task任务开启时,仅调用一次
   */
  protected void setup(Context context
                       ) throws IOException, InterruptedException {
    // NOTHING
  }

  /**
   * Input Split中每个<k,v>都要调用一次map(...)方法。该方法经常需要重写。
  @SuppressWarnings("unchecked")
  protected void map(KEYIN key, VALUEIN value, 
                     Context context) throws IOException, InterruptedException {
    context.write((KEYOUT) key, (VALUEOUT) value);
  }

  /**
   * Task任务结束仅调用一次
   */
  protected void cleanup(Context context
                         ) throws IOException, InterruptedException {
    // NOTHING
  }
  
  /**
   * 熟练的用户会重写该方法,更完全的控制Mapper执行
   */
  public void run(Context context) throws IOException, InterruptedException {
    setup(context);
    try {
      while (context.nextKeyValue()) {
        map(context.getCurrentKey(), context.getCurrentValue(), context);
      }
    } finally {
      cleanup(context);
    }
  }
}

翻译完Mapper类的介绍,发现我们通过各种学习途径了解到的Mapper流程,实际在源码类说明上已经说的清清楚楚了,茅塞顿开。我们看到map(...)方法中每次得到一个KV Pairs:<context.getCurrentKey(),context,getCurrentValue()>。该KV Pairs是FileInputFormat子类的RecordReader提供的,因为该方法在FileInputFormat中没有实现。因此,我们去看FileInputFormat的子类KeyValueTextInputFormat。

/**
 * 一种用于纯文本文件的InputFormat。文件被分成多行,每行以回车和换行结束。每行被分隔符分成key和value。
 * 如果没有这样的分隔符字节,key是整行,value是null。
 * 分隔符可以通过配置文件的mapreduce.input.keyvaluelinerecordreader.key.value.separator指定,默认是tab。
 * 
public class KeyValueTextInputFormat extends FileInputFormat<Text, Text> {

  @Override
  protected boolean isSplitable(JobContext context, Path file) {
    final CompressionCodec codec =
      new CompressionCodecFactory(context.getConfiguration()).getCodec(file);
    if (null == codec) {
      return true;
    }
    return codec instanceof SplittableCompressionCodec;
  }

  public RecordReader<Text, Text> createRecordReader(InputSplit genericSplit,
      TaskAttemptContext context) throws IOException {
    
    context.setStatus(genericSplit.toString());
    return new KeyValueLineRecordReader(context.getConfiguration());
  }

}

在KeyValueTextInputFormat#createRecordReader(InputSplit genericSplit,TaskAttemptContext context)方法中我们看到,在构造KeyValueLineRecordReader的时候,传入了TaskAttemptContext。Mapper中的Context实现了MapContext —> TaskInputOutputContext —> TaskAttemptContext。因此,Mapper#map(context.getCurrentKey(),context.getCurrentValue())来自于KeyValueTextInputFormat#createRecordReader(...)方法返回的KeyValueLineRecordReader。

2.Reducer

/** 
 * 减少具有相同Key的Values的集合
 * 
 * Reducer可以通过JobContext#getConfiguration()方法访问Job的配置

 * Reducer有三个阶段:
 * 1. Shuffle。Reducer通过网络HTTP将Mapper排序后的输出Copy到Mapper端。
 * 2. Sort。框架合并不同的输入,根据Key进行排序。
 *    SecondarySort。如果要对Value进行二次排序,那么应该对key进行扩展。扩展成NewKey{key1,key2}。
 *        然后,指定一个分组比较器。Key将使用NewKey进行排序,但分组要使用分组比较器进行分组。
 *        以决定发送哪些<Key,value>到同一个Reduce调用。分组比较器通过Job#setGroupingComparatorClass(...)指定。
 *        排序通过Job#setSortComparatorClass(...)控制。 
 * 3. Reduce。reduce()方法的输入是<key,[values]>,如果是二次排序的话,values是有序的。     
 *   
 */

public class Reducer<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {

  // Reducer通过Context实现上下文传递
  public abstract class Context 
    implements ReduceContext<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {
  }

  /**
   * Reducer任务开始的时候调用一次
   */
  protected void setup(Context context
                       ) throws IOException, InterruptedException {
    // NOTHING
  }

  /**
   * 对于每个Key,调用一次reduce()方法
   */
  @SuppressWarnings("unchecked")
  protected void reduce(KEYIN key, Iterable<VALUEIN> values, Context context
                        ) throws IOException, InterruptedException {
    for(VALUEIN value: values) {
      context.write((KEYOUT) key, (VALUEOUT) value);
    }
  }

  /**
   * 一个任务最后只调用一次
   */
  protected void cleanup(Context context
                         ) throws IOException, InterruptedException {
    // NOTHING
  }

  
  public void run(Context context) throws IOException, InterruptedException {
    setup(context);
    try {
      while (context.nextKey()) {
        reduce(context.getCurrentKey(), context.getValues(), context);
        // If a back up store is used, reset it
        Iterator<VALUEIN> iter = context.getValues().iterator();
        if(iter instanceof ReduceContext.ValueIterator) {
          ((ReduceContext.ValueIterator<VALUEIN>)iter).resetBackupStore();        
        }
      }
    } finally {
      cleanup(context);
    }
  }
}

Reducer过程中,包含了shuffle、merge、sort、group、reduce、output。先将mapper output拷贝到reduce端,然后进行合并及排序(归并排序)。之后,对相同key的value进行分组,进入reduce()方法中处理,并输出。二次排序见前面的博文详解。

好了,这就是Mapper的执行逻辑了。后续,再分析整个MapTask - Shuffle - ReduceTask过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 首先,要确定MR任务的Mapper和Redcuer进程的个数,需要根据实际的任务需求来确定,通常情况下要考虑计算任务的复杂度和数据量大小,以及系统计算资源的情况,并结合实际的系统环境,来决定Mapper和Redcuer进程的数量。 ### 回答2: 执行MR(MapReduce)任务时,确定MapperReducer进程的个数是根据以下几个因素来决定的: 1. 输入数据大小:输入数据的大小可以决定需要多少个Mapper进程来处理数据。通常情况下,每个输入数据块(HDFS块)由一个Mapper进程处理,因此输入数据块的数量可以用来确定Mapper进程的个数。 2. 集群资源:集群的可用资源也会影响MapperReducer进程的个数。如果集群资源较多,可以增加MapperReducer的数量以提高处理效率。反之,如果集群资源有限,需要限制MapperReducer的数量,以免资源不足导致任务执行缓慢或失败。 3. 任务的性能需求:任务对性能的需求也会影响MapperReducer的个数。如果任务需要快速执行,可以增加MapperReducer的数量以并行处理数据。但是需要注意的是,增加进程数量也会增加任务调度和数据通信的开销,可能导致性能并不线性增加。 4. 数据分布情况:数据分布的均匀程度也会影响MapperReducer的个数。如果数据分布不均匀,可以增加Reducer的个数来加速数据处理过程,以避免某些Reducer成为性能瓶颈。 总之,确定MapperReducer进程的个数需要综合考虑输入数据大小、集群资源、任务性能需求和数据分布情况等因素。通过权衡这些因素,可以选择适当的进程个数来执行MR任务,以提高任务执行效率和性能表现。 ### 回答3: 执行MR任务时,要确定MapperReducer进程的数量,可以依据以下几个方面。 首先,可以考虑输入数据的大小和复杂度。如果输入数据非常大或者非常复杂,那么可能需要增加MapperReducer的数量,以便更好地分配计算资源,提高任务的执行效率。 其次,可以考虑集群的资源情况。如果集群的计算资源较为充足,可以增加MapperReducer的数量,充分利用集群的计算能力,加快任务的执行速度。如果计算资源有限,可以适当减少MapperReducer的数量,以避免资源竞争导致任务执行缓慢。 此外,还可以考虑任务的特点和需求。比如,如果任务需要进行大量的计算和数据处理,可以增加MapperReducer的数量,利用并行计算的优势;如果任务的规模较小或者输入数据分布不均匀,可以减少MapperReducer的数量,以避免资源浪费。 最后,可以通过调优和实验的方式确定最佳的MapperReducer进程数量。可以在小规模数据上进行实验,逐渐增加或减少MapperReducer的数量,观察任务的执行速度和资源利用情况,找到一个合适的平衡点,使得任务在最短的时间内完成,并且充分利用资源。 总之,确定MapperReducer进程数量需要综合考虑输入数据情况、集群资源、任务特点和需求,并通过试验找到最佳的平衡点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值