什么是MapReduce?
主要由Map和Reduce两部分组成。
Reduce阶段在Map阶段执行结束之后执行。
Map阶段的输出结果作为Reduce阶段的输入结果。
Reduce阶段的输入结果对应于多个Map的输出结果。
Reduce阶段计算最终结果并将结果输出。
输入数据:一系列key/value对。
用户实现两个函数,map和reduce。
Map(k,v)list(k1,v1)
Reduce(k,list(v1)) v2
(k1,v1)是中间key/value的结果对。
Output:一系列的(k2,v2)对。
package com.neu.mapreduce.topK; import java.io.IOException; import java.util.TreeMap; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.DoubleWritable; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; 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 com.neu.mapreduce.util.FanData; import com.neu.mapreduce.util.HDFSUtils; public class TopK { public static class TopKMapper extends Mapper<Object, Text, NullWritable, DoubleWritable> { private FanData fanData = new FanData(); private static final int K = 10; private TreeMap<Double, Double> tm = new TreeMap<Double, Double>(); @Override protected void map(Object key,Text value, Context context) throws IOException, InterruptedException { fanData.getInstance(value.toString()); double windSpeed = Double.parseDouble(fanData.getWindSpeed()); tm.put(windSpeed, windSpeed); if(tm.size() > K) { tm.remove(tm.firstKey()); } } @Override protected void cleanup(Context context) throws IOException, InterruptedException { super.cleanup(context); for(Double ws : tm.values()) { context.write(NullWritable.get(), new DoubleWritable(ws)); } } } public static class TopKReducer extends Reducer<NullWritable, DoubleWritable, NullWritable, DoubleWritable> { private static final int K = 10; private TreeMap<Double, Double> tm = new TreeMap<Double, Double>(); @Override protected void reduce(NullWritable key,Iterable<DoubleWritable> values, Context context) throws IOException, InterruptedException { for(DoubleWritable ws : values) { tm.put(ws.get(), ws.get()); if(tm.size() > K) { tm.remove(tm.firstKey()); } } for(Double ws : tm.values()) { context.write(NullWritable.get(), new DoubleWritable(ws)); } } } public static void main(String[] args) throws Exception{ Configuration conf = new Configuration(); HDFSUtils hdfs = new HDFSUtils(conf); hdfs.deleteDir(args[1]); Job job = Job.getInstance(); job.setJarByClass(TopK.class); job.setMapperClass(TopKMapper.class); job.setMapOutputKeyClass(NullWritable.class); job.setMapOutputValueClass(DoubleWritable.class); job.setReducerClass(TopKReducer.class); job.setOutputKeyClass(NullWritable.class); job.setOutputValueClass(DoubleWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true)? 0 : 1); } }
1.数据分片:等长的数据块,每个分片对应一个map任务。分片的大小很大程度影响了map执行的效率。建议分片大小和hdfs数据块大小相同,好处是节省网络开销。
2.Map任务结果并非保存在hdfs:临时数据。Map失败后会在其他节点上启动map。
3.Reduce的任务数量并非由输入数据大小指定,而事实上是独立指定的。Map:reduce=n:m
4.Partition:map输出结果会根据reduce个数进行分区,默认是hash函数来分区,也可以自定义(partition函数)。
5.Shuffle:map和reduce之间的数据流。每个reduce的输入来自于多个map任务的输出。在这个阶段,参数调优对job的运行时间有很大的影响。是奇迹发生的地方。
shuffle 是什么?过程?
在MapReduce过程中需要将各个节点上的同一类数据汇集到一个节点进行计算。把这些分布在不同节点的数据按照一定规则聚集到一起的过程,就称之为shuffle(Shuffle是Map和Reduce之间的操作,Shuffle 过程本质上就是将 Map 端获得的数据使用分区器进行划分,并将数据发送给对应的 Reducer 的过程)。