MapReduce组件概述_学习笔记_未完成
一、组成
Map:负责初步处理数据
Reduce:负责处理map传递过来的数据
Partitionner :负责数据的筛选分区,使得数据份文件存储
Combiner:使得数据提前排序可大大减少map传递到reduce的数据
除了上诉所说在程序编写时一般还会牵扯到一个Driver(任务配置)与实体类
二、Map:
1、概述:Map阶段主要将数据按条处理,并将key与value向后传递。
2、代码实现:
a、继承map类重写map方法指定其输入输出数据类型。
b、在job对象上指定Map
public class RainDataMapper extends Mapper<LongWritable, Text,RainData, NullWritable> {
@Override
protected void map(LongWritable key, Text value, Context context) throws Exception {
// 数据的转换->切片
List<String> list = Arrays.asList(value.toString().split("\t"));
{
// 数据处理
}
//写出
context.write(rainData,NullWritable.get());
}
}
3、MapTask的个数是多少?
MapTask的个数取决于切片数,切片数取决于文件个数以及文件大小且切片是逻辑上的。同时片大小默认等于块大小,换句话说当我们操作数据用到片时直接操作了一个块,程序会去判断每被操作一个文件要切成几个片,如果这个文件小于1.1倍的块大小那么不切反之切片。
其中块大小在本地默认是32M在集群上默认是128M。
其实对于MapTask的个数我们还可以联想到之前所学DataNode时Block块的大小起来学习。在此前我们探讨Block块大小时得知,一个块同时只能执行一个任务如果这个块设置的过大那么效率很明显会降低。而这个任务就是Map。
三、Reduce
1、概述:Reduce阶段主要是将按组获得到的有序数据进行处理,并将处理完毕的数据写入对应的文件。
2、代码实现:
a、继承 Reducer类重写Reducer方法同时指定输入输出参数
b、在job对象上指定Reduce
public class RainDataReducer extends Reducer<RainData, NullWritable,RainData,NullWritable> {
@Override
protected void reduce(RainData key, Iterable<NullWritable> values, Context context)
throws IOException, InterruptedException {
{
// 业务处理
}
// 内容写出
context.write(rainData,NullWritable.get());
}
}
3、最终输出文件的个数是多少?
我们最终输出文件得个数取决于Reduce的个数,此参数可在下文中的Driver中介绍到。
四、Partitionner
1、概述:此阶段将map的数据分区并传递给Reduce,其中getPartition方法返回的数值就是所要存储文件的编号或者说是对应的reduce。
2、代码实现:
a、继承Partitionner类重写 getPartition()方法,然后通过操作return的数值决定数据的分区。
b、在job对象上指定分区函数
c、设置reduce的个数
public class RainDataPartitioner extends Partitioner<RainData, NullWritable> {
@Override
public int getPartition(RainData rainData, NullWritable nullWritable, int numPartitions) {
/**
* 奇数年 指定一个分区 偶数年指定一个分区
*/
return rainData.getYear()%2;
}
}
3、Partitionner与Reduce的关系
在使用时我们不免要思考如果我们的分区返回值与Reduce的编号或者说个数不匹配怎么办?比如分区函数返回了3但是我们只有两个reduce怎么办。其实我们只需要把握住getPartition()的返回值小于Reduce的个数即可(Reduce编号从0开始)。
此外还有一点需要注意,即只有一个reduce的话不会调用分区规则。也就是说如果只有一个输出文件的话那么就不会调用我们所指定的分区函数getPartition()。
五、排序
1、概述:此阶段可以将map输出的数据进行排序。
2、代码实现:
方法一:a、实体类实现WritableComparable接口的compareto方法(一个参数)
public class RainData<toString> implements WritableComparable<RainData>{
private int year;
private int month;
private int number;
@Override
public void write(DataOutput out) throws IOException {
out.writeInt(year);
out.writeInt(month);
out.writeInt(number);
}
@Override
public void readFields(DataInput in) throws IOException {
this.year = in.readInt();
this.month = in.readInt();
this.number = in.readInt();
}
// 重写此方法即可
@Override
public int compareTo(RainData o) {
return Integer.compare(this.year,o.getYear())!= 0?
(-Integer.compare(this.year,o.getYear())):(Integer.compare(this.month,o.getMonth()));
}
}
方法二:a、继承WritableComparator类,实现compare方法(俩参数)
b、driver中指定该类同时实现实体类中的WritableComparable的相关方法
(只不过不走 这个比较器罢了)
public class MyComparable extends WritableComparator {
public MyComparable(){
super(RainData.class,true);
}
@Override
public int compare(WritableComparable a, WritableComparable b) {
return a.compareTo(b);// 此地任意指定,我们这书写等于没写
}
}
3、注意
对于自己实现的实体类来说,当我们指定了比较器类时,如果此类没有实现compareto方法则调用实体类本身的比较器;如果实现了那么调用此类的方法。换句话说对于我们自己写的实体类我们必须要序列化、反序列化实体类同时必须要实现compare方法。
六、实体类
1、概述:我们上述所讨论的key都是对应着系统所编写好的类比如Text类,但是当我们使用自己书写的实体类作为key时我们必须进行一定的操作。
2、我们在MapReduce中操作的key默认都有序列化与反序列化以及比较的行为
a、序列化与反序列化
概述:将对象转换成字节数据,通过网络或者io流进行传输
序列化 :将数据解析为一个固定的结构
反序列化:将数据读取出来
实现:writable接口重写read、write方法序列化与反序列化顺序要一致
此外与java中Serializable相比Hadoop的序列化与反序列化更加紧凑,java更加繁杂,相对比 较重量级。java在序列化的过程中会将类的额外信息进行序列化这部分恰是我们不需要的。
b、实现writeablecomparable接口
在比较排序时我们所传递的参数是riteablecomparable类型的引用换句话说我们传递的对象必须是此类的子类。
public class RainData<toString> implements WritableComparable<RainData>{
private int year;
private int month;
private int number;
// 序列化
@Override
public void write(DataOutput out) throws IOException {
out.writeInt(year);
out.writeInt(month);
out.writeInt(number);
}
// 反序列化
@Override
public void readFields(DataInput in) throws IOException {
this.year = in.readInt();
this.month = in.readInt();
this.number = in.readInt();
}
// 排序
@Override
public int compareTo(RainData o) {
return Integer.compare(this.year,o.getYear())!= 0?
(-Integer.compare(this.year,o.getYear())):(Integer.compare(this.month,o.getMonth()));
}
}