Hadoop备忘:Reduce阶段Iterable<VALUEIN> values中的每个值都共享一个对象

本文深入探讨Hadoop MapReduce框架中Reduce阶段的工作原理,特别是如何处理具有相同key的多个value,以及在迭代过程中可能出现的对象引用问题。对于自定义Writable类型的开发者来说,理解这一过程对于避免数据处理错误至关重要。
 /**
   * Iterate through the values for the current key, reusing the same value 
   * object, which is stored in the context.
   * @return the series of values associated with the current key. All of the 
   * objects returned directly and indirectly from this method are reused.
   */
  public 
  Iterable<VALUEIN> getValues() throws IOException, InterruptedException {
    return iterable;
  }


在Reduce阶段,具有相同key的的所有的value都会被组织到一起,形成一种key:values的形式。


一般情况下,我们会针对某个key下的所有的values进行处理,这里需要注意一个问题,当我们写下如下代码的时候:

  protected void reduce(KEYIN key, Iterable<VALUEIN> values, Context context
                        ) throws IOException, InterruptedException {
    for(VALUEIN value: values) {
      context.write((KEYOUT) key, (VALUEOUT) value);
    }
  }

我们在一个循环中,每次得到的value实际上都是指向的同一个对象,只是在每次迭代的时候,将新的值反序列化到这个对象中,以更新此对象的值:

  /**
   * Advance to the next key/value pair.
   */
  @Override
  public boolean nextKeyValue() throws IOException, InterruptedException {
    if (!hasMore) {
      key = null;
      value = null;
      return false;
    }
    firstValue = !nextKeyIsSame;
    DataInputBuffer nextKey = input.getKey();
    currentRawKey.set(nextKey.getData(), nextKey.getPosition(), 
                      nextKey.getLength() - nextKey.getPosition());
    buffer.reset(currentRawKey.getBytes(), 0, currentRawKey.getLength());
    key = keyDeserializer.deserialize(key);
    DataInputBuffer nextVal = input.getValue();
    buffer.reset(nextVal.getData(), nextVal.getPosition(),
        nextVal.getLength() - nextVal.getPosition());
    value = valueDeserializer.deserialize(value);

    currentKeyLength = nextKey.getLength() - nextKey.getPosition();
    currentValueLength = nextVal.getLength() - nextVal.getPosition();

    hasMore = input.next();
    if (hasMore) {
      nextKey = input.getKey();
      nextKeyIsSame = comparator.compare(currentRawKey.getBytes(), 0, 
                                     currentRawKey.getLength(),
                                     nextKey.getData(),
                                     nextKey.getPosition(),
                                     nextKey.getLength() - nextKey.getPosition()
                                         ) == 0;
    } else {
      nextKeyIsSame = false;
    }
    inputValueCounter.increment(1);
    return true;
  }

为什么要注意这种情况呢?正如本文开始引用的那段Hadoop源代码中的注释所指明的,这里主要涉及到引用。如果值的类型是自己实现的某种Writable,比如说AType,并且在其中持有对其它对象的引用,同时,在Reduce阶段还要对相邻的两个值(current_value,value)进行同时进行操作,这个时候,如果你仅仅是将value强制类型转换成相应的AType,这个时候,current_value和value其实是指向的同一段内存空间,也就是说,当我们迭代完第一次的时候,current_value缓存了当前的值,但是当进行下一次迭代,取到的value,其实就是将它们共同指向的那段内存做了更新,换句话说,current_value所指向的内存也会被更新成value的值,如果不了解Reduce阶段values的迭代实现,很可能就会造成莫名其妙的程序行为,而解决方案就是创建一个全新的对象来缓存“上一个”值,从而避免这种情况。


希望对遇到类似问题的同学有帮助!



你提供的 `mapred-site.xml` 配置文件中存在 **重复定义** 的问题,并且包含了一个 **占位符 `${full path of your hadoop distribution directory}`**,这个占位符不会被系统识别,需要替换为实际路径。 --- ## ❌ 问题分析 ### 1. **重复的配置项** 你重复定义了以下属性: ```xml <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> ``` 以及: ```xml <property> <name>yarn.app.mapreduce.am.env</name> <value>HADOOP_MAPRED_HOME=${full path of your hadoop distribution directory}</value> </property> ... ``` 这些重复项会导致配置混乱,甚至被忽略。 --- ## ✅ 正确的 `mapred-site.xml` 配置应如下: ```xml <configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> <property> <name>yarn.app.mapreduce.am.env</name> <value>HADOOP_MAPRED_HOME=/root/software/hadoop-3.3.4</value> </property> <property> <name>mapreduce.map.env</name> <value>HADOOP_MAPRED_HOME=/root/software/hadoop-3.3.4</value> </property> <property> <name>mapreduce.reduce.env</name> <value>HADOOP_MAPRED_HOME=/root/software/hadoop-3.3.4</value> </property> </configuration> ``` --- ## 🧪 配置说明 | 配置项 | 说明 | |--------|------| | `mapreduce.framework.name` | 设置 MapReduce 使用 YARN 作为执行框架 | | `yarn.app.mapreduce.am.env` | 设置 ApplicationMaster 的环境变量,告诉它 Hadoop 的 MapReduce 安装路径 | | `mapreduce.map.env` | 设置 Map Task 的环境变量 | | `mapreduce.reduce.env` | 设置 Reduce Task 的环境变量 | --- ## 🛠️ 后续操作建议 1. **编辑 `mapred-site.xml` 文件,删除重复项和占位符行** 2. **确保所有节点的 `mapred-site.xml` 都同步** 3. **编辑 `hadoop-env.sh`,设置 `HADOOP_MAPRED_HOME`:** ```bash export HADOOP_MAPRED_HOME=/root/software/hadoop-3.3.4 ``` 4. **重启 Hadoop 服务:** ```bash stop-yarn.sh stop-dfs.sh start-dfs.sh start-yarn.sh ``` 5. **运行 MapReduce 测试任务验证:** ```bash hadoop jar /root/software/hadoop-3.3.4/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.3.4.jar TestDFSIO -write -nrFiles 10 -size 10MB ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值