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

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

原创  2016年03月31日 16:11:22
[java]  view plain  copy
  1. /** 
  2.   * Iterate through the values for the current key, reusing the same value  
  3.   * object, which is stored in the context. 
  4.   * @return the series of values associated with the current key. All of the  
  5.   * objects returned directly and indirectly from this method are reused. 
  6.   */  
  7.  public   
  8.  Iterable<VALUEIN> getValues() throws IOException, InterruptedException {  
  9.    return iterable;  
  10.  }  


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


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

[java]  view plain  copy
  1. protected void reduce(KEYIN key, Iterable<VALUEIN> values, Context context  
  2.                       ) throws IOException, InterruptedException {  
  3.   for(VALUEIN value: values) {  
  4.     context.write((KEYOUT) key, (VALUEOUT) value);  
  5.   }  
  6. }  

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

[java]  view plain  copy
  1. /** 
  2.  * Advance to the next key/value pair. 
  3.  */  
  4. @Override  
  5. public boolean nextKeyValue() throws IOException, InterruptedException {  
  6.   if (!hasMore) {  
  7.     key = null;  
  8.     value = null;  
  9.     return false;  
  10.   }  
  11.   firstValue = !nextKeyIsSame;  
  12.   DataInputBuffer nextKey = input.getKey();  
  13.   currentRawKey.set(nextKey.getData(), nextKey.getPosition(),   
  14.                     nextKey.getLength() - nextKey.getPosition());  
  15.   buffer.reset(currentRawKey.getBytes(), 0, currentRawKey.getLength());  
  16.   key = keyDeserializer.deserialize(key);  
  17.   DataInputBuffer nextVal = input.getValue();  
  18.   buffer.reset(nextVal.getData(), nextVal.getPosition(),  
  19.       nextVal.getLength() - nextVal.getPosition());  
  20.   value = valueDeserializer.deserialize(value);  
  21.   
  22.   currentKeyLength = nextKey.getLength() - nextKey.getPosition();  
  23.   currentValueLength = nextVal.getLength() - nextVal.getPosition();  
  24.   
  25.   hasMore = input.next();  
  26.   if (hasMore) {  
  27.     nextKey = input.getKey();  
  28.     nextKeyIsSame = comparator.compare(currentRawKey.getBytes(), 0,   
  29.                                    currentRawKey.getLength(),  
  30.                                    nextKey.getData(),  
  31.                                    nextKey.getPosition(),  
  32.                                    nextKey.getLength() - nextKey.getPosition()  
  33.                                        ) == 0;  
  34.   } else {  
  35.     nextKeyIsSame = false;  
  36.   }  
  37.   inputValueCounter.increment(1);  
  38.   return true;  
  39. }  

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


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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值