hadoop 0.20.2 api里面,作业被重新定义到了类 org.apache.hadoop.mapreduce.Job。
它有3个特别的方法:
job.setPartitionerClass(Partitioner p);
job.setSortComparatorClass(RawComparator c);
job.setGroupingComparatorClass(RawComparator c);
数据在被map处理过之后,会根据 Partitioner 定义的规则,把中间结果分发到特定的reduce。
Partitioner
public static class P extends Partitioner{ @Override public int getPartition(IdWithTimestamp k, Value v, int parts) { int hash = k.getId().hashCode(); return (hash & Integer.MAX_VALUE) % parts; } }
接下来,可以做一些控制,使得不同 Key的中间数据,可以打包成一组,在一次reduce()调用中处理,setGroupingComparatorClass提供分组规则。
补充:默认实现是以整个key作为分组标识的。但是,一个比较典型的情况是,如果你在key中加入了额外的字段,用于排序,就需要手工调整group了。顺序很重要!
例如,key可能是[id, timestamp],这样可以使相同的id(String)能按时间戳(long)排序,现在拆出包含在key里的相同id,分到相同的组。
public class IdComparator extends TextComparator { @Override public int compare(IdWithTimestamp o1, IdWithTimestamp o2) { return o1.getId().compareTo(o2.getId()); } }
然后,中间数据在被reduce处理之前,会先被集中排序,setSortComparatorClass提供排序规则。
public class TimestampComparator extends TextComparator { @Override public int compare(IdWithTimestamp o1, IdWithTimestamp o2) { return o1.getTimestamp() - o2.getTimestamp(); } }
如果不提供这些东西,默认使用的是 Key类型的 compareTo方法,而不是 hashCode和 equals。
可以自定义 WritableComparable类型,提供优化比较方法,以提高执行效率。。。字节比较,那是个挺麻烦的事情,我都是很偷懒地一直用Text,拼接、拆分字符串做比较。
Partitioner,SortComparator 和 GroupingComparator 配合即可实现某些对顺序有要求的计算。如果数据集很大的话(reduce输入超过HDFS块儿大小),为保证之前任务的顺序输出,就需要在再次m/r处理的时候控制map对输入的分割
public class NoSplitInputFormat extends TextInputFormat { @Override protected boolean isSplitable(JobContext context, Path file) { return false; } }
排序 + 计算 两个任务,不等于一个完整任务里的 按顺序计算。
实践证明,使用外部的RawComparator 更具灵活性,可以提供不同的排序和分组策略。
附例子程序,测试数据,测试结果
Join的时候也用得着它们
enjoy it!