第一题:请简述一下shuffle-map端的流程
每个map任务都会有一个环形缓冲区存储数据。环形缓冲区的默认大小为100MB,阈值为80MB。当达到阈值时,会有一个后台线程把内容溢写到磁盘中。剩下的数据继续写入剩余20MB中。如果20MB写满后会进入阻塞状态。
在溢写前,会先根据分区器的逻辑将数据分区。每个分区在内存中会进行排序(QuickSort,默认是字典排序)。如果指定了combiner函数,那么它就在排序后的输出上运行。使得map输出更加紧凑
如果至少存在3个溢出文件时,则combiner就会在输出文件写到磁盘之前再次运行。如果只有1或2个溢出文件的话。不会运行map。最终合并(归并算法)成一个最终的临时文件。然后写入到磁盘。
为了使磁盘读写的速度更快,节约磁盘空间。在溢写到磁盘前对数据进行压缩,在进行传输
第二题:请简述一下shuffle-reduce端的流程
在每个mapTask完成后,reduceTask会利用线程开始fetch属于自己要处理的分区的数据,线程默认是5个,线程数量是针对每一个节点来说的,线程通过http协议抓取数据
如果抓过来的数据相当小,会被复制到reduce任务JVM的内存中进行归并排序。输入给reduce函数
如果数据量过大,则会直接拷贝到本地磁盘上,然后讲多个文件合并成较大的文件,合并因子是10,合并时采用的算法是归并算法。(最后一次合并一定要满足10这个因子,而且不会溢写成文件,直接输出给reduce)
在归并算法合并时,如果map端的数据是压缩的,那么要在reduceTask的内存中解压缩在合并。
reduce处理后将数据存储到HFDS上。
第三题:问题描述
利用MR将所有的电影信息进行排序,排序规则:先按照uid排序,如果相同,按照电影id排序(这就是二次排序)
代码
package mr.MovieRating;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* 根据数据格式:{"movie":"1193","rate":"5","datetime":"978300760","uid":"1"}
* 可以封装成一个hadoop类型RateBean,一个对象对应一行记录
*/
public class MovieBean implements WritableComparable<MovieBean> {
private int movie;
private int rate;
private String datetime;
private int uid;
public MovieBean() {
}
public MovieBean(int movie, int rate, String datetime, int uid) {
this.movie = movie;
this.rate = rate;
this.datetime = datetime;
this.uid = uid;
}
public int getMovie() {
return movie;
}
public void setMovie(int movie) {
this.movie = movie;
}
public int getRate() {
return rate;
}
public void setRate(int rate) {
this.rate = rate;
}
public String getDatetime() {
return datetime;
}
public void setDatetime(String datetime) {
this.datetime = datetime;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
@Override
public int compareTo(MovieBean o) {
return uid == o.uid ? o.movie - movie : o.uid - uid;
}
@Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeInt(movie);
dataOutput.writeInt(rate);
dataOutput.writeUTF(datetime);
dataOutput.writeInt(uid);
}
@Override
public void readFields(DataInput dataInput) throws IOException {
movie = dataInput.readInt();
rate = dataInput.readInt();
datetime = dataInput.readUTF();
uid = dataInput.readInt();
}
@Override
public String toString() {
return uid + "\t" + movie + "\t" + rate;
}
}
package mr.MovieRating;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.codehaus.jackson.map.ObjectMapper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class RateDriver {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
job.setJarByClass(RateDriver.class);
job.setMapperClass(RateMapper.class);
job.setReducerClass(RateReduce.class);
job.setOutputKeyClass(MovieBean.class);
job.setOutputValueClass(NullWritable.class);
job.setGroupingComparatorClass(RateComparator.class);
FileInputFormat.addInputPath(job, new Path("src\\main\\resources\\rating.json"));
Path outputPath = new Path("D:/output");
FileSystem fs = FileSystem.get(conf);
if (fs.exists(outputPath)) {
fs.delete(outputPath, true);
}
//使用args参数的第二个元素充当输出路径,灵活
FileOutputFormat.setOutputPath(job, outputPath);
//提交作业
System