1、自定义bean中的CopmareTo()
public class KeyPair implements WritableComparable<KeyPair> {
private int year;
private int hot;
@Override
/**
* 定义CopmareTo()是在溢出和merge时用来来排序的
*/
public int compareTo(KeyPair o) {
int res = Integer.compare(year, o.getYear());
if (res != 0) {
return res;
}
return Integer.compare(hot, o.getHot());
}
@Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.write(year);
dataOutput.write(hot);
}
@Override
public void readFields(DataInput dataInput) throws IOException {
this.year = dataInput.readInt();
this.hot = dataInput.readInt();
}
@Override
public int hashCode() {
return (year + "/" + hot).hashCode();
}
@Override
public boolean equals(Object obj) {
KeyPair k = (KeyPair) obj;
int res1 = Integer.compare(year, k.getYear());
int res2 = Integer.compare(hot, k.getHot());
return (res1 == 0 && res2 == 0) ? true : false;
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("year", year)
.append("hot", hot)
.toString();
}
get和set方法...
}
2、job.setGroupingComparatorClass(ItemidGroupingComparator.class)
GroupingComparator是在reduce阶段分组来使用的,由于reduce阶段,如果key相同的一组,只取第一个key作为key,迭代所有的values。
如果reduce的key是自定义的bean,我们只需要bean里面的某个属性相同就认为这样的key是相同的,并把value放同一个values,进行一次reduce函数的处理,这时我们就需要自定义GroupCoparator来“欺骗”reduce了。
3、job.setPartitionerClass(ItemIdPartitioner.class)
partitioner执行时机:shuffle阶段,在mapper执行完成,Reducer还没有执行的时候,mapper的输出就是partitioner的输入 即<k2,v2>
目的:shuffle阶段,用来决定map输出时,什么样的key输出到同一个reduce节点
4、job.setSortComparatorClass(SortHot.class)
目的:对进入同一个reduce的键或键的部分 进行排序
时机:shuffle阶段,reduce部分fetch数据之后,合并小文件之后的排序
5、job.setCombinerClass()
时机:map输出之后,shuffle之前
目的:每一个map可能会产生大量的输出,combiner的作用就是在map端对输出先做一次合并,以减少传输到reducer的数据量
combiner有类似于本地reducer的功能。如果不使用combiner,那么所有的结果都是Reduce完成,效率会相对低下。使用combiner,先完成的map会在本地聚合,提升速度。
注意:Combiner的输出是Reducer的输入,如果combiner是可插拔的,添加combiner绝对不能改变最终计算结果。所以在可插拔的情况下Combiner只应该用于那种和Reduce的输入key/value与输出key/value类型完全一致,且不影响最终结果的场景。比如累加,最大值等。
6、FileInputFormat.addInputPath(job, new Path("/usr/input/hot"))和FileOutputFormat.setOutputPath(job, new Path("/usr/output/hot"))
设置源数据的输入目录,和计算结果的输出目录
6、Shuffle原理!
一个切片对应一个map ,每一个map对应一个在内存中的环形缓冲区,用来存储map的输出,缓冲区的大小默认是100M
当map向环形缓冲区写入数据达到一定阀值,就是超出一定范围(80%) 就会启动一个后台线程将缓冲区数据溢写到磁盘
注意:不仅仅是将数据存入到磁盘,而是经过很复杂的过程,写入磁盘之前首先将数据按照分区规则进行分区,如果没有指定分区规则,就会按照Hadoop默认的分区规则进行分区,然后按照排序规则对分区内的数据按照k2(map输出的key即reducer输入的key)进行排序(每个分区内调用job.setSortComparatorClass()设置的key比较函数类排序(如果没有通过job.setSortComparatorClass()设置key比较函数类,则使用key的实现的compareTo方法),如果k2是Text类型 则按照k2的字典顺序的排序规则,如果是Bean按照自定义的排序规则排序,然后得到多个分区且排序的小文件。分区是按照分区号排序,分区内的数据是按照k2的排序规则进行排序,小文件内可以有多个分区。
由于map向缓存区中存数据速度远远比缓冲区向磁盘写数据的速度,所以当缓冲区中的数据达到80%,map就会阻塞,停止向缓冲区存入数据,直到缓冲区中的数据写入到多个小文件并清空缓冲区,才让map继续向缓冲区存入数据,之所以写入多个小文件,是因为文件越小排序的速度越快。
注意:一个分区如果过大可能存在于多个小文件之中。在得到多个分区且排序的小文件后要进行合并,合并规则是按照分区号将多个小文件中的部分分区数据合并成对应分区号的完整分区数据里面,在合并的同时再对分区内的多个部分数据按照k2排序规则进行一次排序。