说明:本文提及的所有观点和代码,均基于Hadoop 2.5.0-cdh5.2.0环境。
1. WritableComparable和WritableComparator的区别
WritableComparable和WritableComparator都提供了比较的功能。先看看其相关定义:
public interface WritableComparable<T> extends Writable, Comparable<T>
public class WritableComparator implements RawComparator, Configurable
public interface RawComparator<T> extends Comparator<T>
Comparable提供了int compareTo(T o)方法;RawComparator提供了int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2)和int compare(T o1, T o2)方法,其中后者继承自Comparator。
从源码来看:WritableComparable并没有实现其继承的compareTo方法,所以如许多人所说“WritableComparable只是声明自己提供了compare服务”。而WritableComparator实现了两个compare方法,所以如许多人所说“WritableComparator是提供了compare服务的一个具体实现方法”。
从定义形式看:前者的定义彰显主动形式,即拿自己和别的对象比较,这样的形式更具有面向对象的思维。后者的定义彰显工具类的色彩,这样的形式更具有面向过程的思维。
默认情况下,MapReduce根据输入记录的键对数据排序。键的排列顺序是由RawComparator控制的,规则如下:
a. 若属性Job#setSortComparatorClass已设置,则使用该类的实例;
b. 否则,key必须是WritableComparable的子类,使用注册于其上的comparator;
c. 如果还没有注册的comparator,则使用RawComparator将字节流反序列化为WritableComparable一个对象,再调用其compareTo()方法排列。
从这个规则,我们应该明白,WritableComparator应该在性能上优于WritableComparable。使用中,也印证了这点。测试中,我使用一个自定义的Writable(继承自WritableComparable),包括两个成员变量first和second,基于15G的HDFS文件,在其余条件都相同的前提下,当然最终结果也相同了......MR运行耗时如下:
数据类型 | 排序方法 | 耗时 |
first为String,second为long | 自定义Writable的compareTo方法 | 2mins, 9sec |
first为Text,second为LongWritable | 自定义Writable的compareTo方法 | 1mins, 58sec |
first为Text,second为LongWritable | 自定义WritableComparator的compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2)方法 | 1mins, 42sec |
2. 如果使用了combiner,其输出KV的类型,必须和map输出KV类型严格保持一致
个人感觉,这个约束有点死。因为map->combiner->redcue,这是典型的处理次序,只要相邻的两个环节保持一致即可,即前者的输出和后者的输入类型一致即可。完全没有必要map的输出,就限制了后面两个环节的输入类型。我们也尝试了将map输出value设为Writable类型,而真实写入的是其子类,例如LongWritable,还是会因类型不一致而报错,看来MR源码中还是做了严格的类型一致性判断。
3. 建议使用GenericOptionsParser重新处理参数
例如对于
public static void main(final String[] args)
{
final Configuration conf = new Configuration();
final String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
}
调用方式:hadoop jar TestLibJar.jar -libjars sqljdbc4-1.0.jar inputpath outputpath
otherArgs[0]为"inputpath",args[0]为"-libjars"。
4. Hadoop的Distributed Cache只适用于MR
使用Hadoop的Distributed Cache(DC)机制,可以访问外部的文件,具体操作方法可见我的博文“MapReduce中如何访问外部jar包和数据文件”。但是在MR框架代码之外访问DC不会生效,例如:在main函数中,启动MR Job前后使用了第三方jar,则仍然可能会找不到相关类。
顺便说明一个小问题:看到许多人说,使用DC时,必须让MR的主类extends Configured implements Tool。但是我发现即使不这么做,也是可以的,或许是因为hadoop版本原因吧。
5. MR运行报错:Illegal partition for "key" (-1)
错误分析:该错误一般是由于自定义Partitioner#getPartition导致的。错误提示中的key和-1,分别是getPartition计算依赖的参数和返回值,即将该key分配给编号为-1的reduce进行处理。因为MR框架要求getPartition返回值为[0,reduce数目-1],所以发生了上述错误。
解决方案:修改你自定义Partitioner#getPartition的实现逻辑,确保其返回值为[0,reduce数目-1]。此处需要注意一点:许多人使用了hashCode方法,但是其可能会返回负值;或者调用父Partitioner的getPartition实现逻辑:
return iPartitionID = super.getPartition(new Text(strDayPrefix), value, numberPartions);
总之一句话,确保getPartition返回值为[0,reduce数目-1]即可......