Hadoop原理汇总(二)——MapReduce

5 MapReduce

5.1 设计理念

计算向数据靠拢,减小节点间的数据移动开销

  1. 前提条件
    ● 待处理的数据集可以分解为许多小数据集
    ● 每个小数据集都可以完全地并行处理

5.2 基础架构

  MapReduce框架包含一个Master和若干个Slave。Master上运行JobTracker,负责作业和任务的调度,并监控它们的执行;Slave上运行TaskTracker,负责执行由JobTracker指派的任务。
  通常,TaskTracker与DataNode运行于同一节点,JobTracker与NameNode可以运行于同一节点,也可以不运行于同一节点。

这里写图片描述

5.3 工作流程概述

(1)首先使用InputFormat模块做Map前预处理,将输入文件进行逻辑切分,得到基本输入单位InputSplit(InputSplit中记录了要处理数据的位置和长度);
(2)RecorderReader根据InputSplit中的信息,加载数据,并转换为适合Map输入的键值对;
(3)Map任务对数据进行处理,输出一系列<key, value>作为中间结果;
(4)Shuffle过程:对中间结果进行分区、排序、合并、归并等操作,得到<key, value-list>;此过程在Map端和Reduce端都有执行;
(5)Reduce过程将Shutffle的结果作为输入,执行用户定义的逻辑,输出结果给OutputFormat模块;
(6)OutputFormat模块对输出目录以及输出结果类型进行验证,验证通过后将结果输出到DSF中。

这里写图片描述

6 shuffle过程

6.1 过程简介

这里写图片描述

6.2 Map端shuffle过程

这里写图片描述

(1)Map处理:Map任务接收<key, value>作为输入,按一定规则转换成<key, value>进行输出。

(2)写入缓存:Map任务的输出结果首先被写入缓存,缓存默认大小是100MB,溢写比例是0.8,即当缓存使用80MB时就开始溢写操作,保证了Map结果可以一直写入缓存而不受溢写过程的影响。
(3)溢写操作:溢写到磁盘之前,会依次进行分区(Partition)、排序(Sort)、合并(Combine)等操作,操作结束后会写入磁盘并清空缓存:

(1)MapReduce通过Partitioner接口对<k,v>进行分区,默认分区规则是hash(key) mod R,把结果分给R个Reduce任务;
(2)对每个分区内的所有<k, v>,后台进程根据key进行排序;
(3)排序过后是一个可选的合并操作,需要用户事先定义Combiner函数。将具有相同键的<k1, v1>,<k1, v2>的value相加,得到<k, v1+v2>,减小溢写数据量。并非所有场合都能使用Combiner操作;

(4)归并(Merge):对磁盘中的多个溢写文件进行归并操作,生成一个大的溢写文件。对具有相同key的<k1, v1>、<k1, v2>归并成一个新的键值对<k1, <v1,v2>>

6.3 Reduce端shuffle过程

这里写图片描述

(1)领取数据:每个Reduce任务会不断地通过RPC向JobTracker询问Map任务是否已经完成,一旦收到JobTracker的通知,Reduce任务就会到该Map任务所在的机器上把属于自己处理的分区数据领取到Reduce任务的缓存中。
(2)归并数据:缓存中的数据来自于不同的Map任务,缓存占满时启动溢写操作。首先对<k, v>进行归并;然后进行合并操作,减少写入磁盘数据量,最后将数据写入磁盘生成溢写文件。磁盘中的多个溢写文件会被再次归并成一个大文件,每轮归并操作可以归并的文件数量由参数io.sort.factor决定,默认是10。
(3)Reduce处理:归并后的大文件直接输入给Reduce任务,执行reduce函数。

6.4 sort过程

在MapReduce计算框架中主要用到了两种排序方法:快速排序和归并排序。在Map任务和Reduce任务过程中,一共发生了3次排序操作。
(1)在Map端溢写操作中,存在分区、排序、合并等操作,其中包含一次排序过程。是在缓冲区进行的内排序,属于快速排序;
(2)在Map任务完成之前,会对Map端磁盘中的多个溢写文件进行归并操作,由于单个溢写文件已经有序,所以本次进行的是归并排序;
(3)Reduce端从多个Map端领回溢写文件后,需要对这些溢写文件进行再次排序,属于归并排序。

6.5 MapReduce任务的分配

(1)任务槽
  每个TaskTracker有固定数量的任务槽(slot)来处理Map和Reduce,任务槽的数量由计算机的内核数和内存大小决定的。调度器只有在TaskTracker的Map槽被map任务填满的情况下,才会分配reduce任务到Reduce槽。

(2)本地化
  Map任务过程中涉及数据本地化和机架本地化:数据本地化是指任务在输入数据分片所在的计算机上运行,即“移动计算比移动数据划算”;机架本地化是指任务和输入数据分片不在同一个节点上,但在同一个机架上。JobTracker运行Map任务时,首先选择数据本地化,然后选择机架本地化。

  Reduce任务不需要考虑本地化问题,因为可能有多个Map任务输出到一个Reduce任务来处理。

6.6 任务的分配

(1)推测执行
  多任务在多节点执行时,很有可能因为某个任务执行效率非常缓慢,出现“拖后腿“的现象。推测执行是指当检测到有任务比预期慢时,会调度空闲的节点执行剩余任务的复制,优化执行过程
  当任务完成时,节点向JobTracker通告,如果其他的复制任务还在执行,则JobTracker通知TaskTracker结束这些任务并丢弃它们。
  推测执行虽然可以减少作业的执行时间,但却降低了集群的执行效率,减少了吞吐量。推测执行默认是开启的,Job类的setSpeculativeExecution(boolean speculativeExecution)方法可以设置是否开启推测执行。

(2)JVM重用
  当每个任务的执行时间都很短时,可以重用JVM来优化性能。JVM重用可以使同一个job的一些静态数据得到共享,从而提升集群性能。但JVM重用也会带来JVM中碎片增加问题,这对JVM来说影响不是很大。
  jobconf中的setNumTasksToExcutePerJvm()方法也可以使用上次的值。

(3)跳过坏的记录
Hadoop检测出来的坏记录以序列文件的方式保存在_log/skip,在作业完成后可以查看这些记录。

6.7 MapReduce的Java API

(1)org.apache.hadoop.mapreduce.Job

public class Job extends org.apache.hadoop.mapreduce.task.JobContextImpl implements JobContext {
    //获取实例
    public static Job getInstance(Configuration conf) throws IOException {}//
    public static Job getInstance(Configuration conf, String jobName) throws IOException{}
    public static Job getInstance(JobStatus status, Configuration conf) throws IOException {}

    //获取属性字段
    public long getStartTime(){}
    public long getFinishTime() throws IOException, InterruptedException{}
    public JobPriority getPriority() throws IOException, InterruptedException
    public String getJobName(){}
    public String getJobFile(){}

    //set方法
    /********** 必要方法 **************/
    public void setJarByClass(Class<?> cls){}
    public void setMapperClass(Class<? extends Mapper> cls) throws IllegalStateException{}
    public void setReducerClass(Class<? extends Reducer> cls) throws IllegalStateException{}

    public void setOutputKeyClass(Class<?> theClass) throws IllegalStateException {}
    public void setOutputValueClass(Class<?> theClass) throws IllegalStateException {}

    /*********** 可选方法 *************/
    public void setInputFormatClass(Class<? extends InputFormat> cls) throws IllegalStateException {}
    public void setOutputFormatClass(Class<? extends OutputFormat> cls) throws IllegalStateException{}

    public void setMapOutputKeyClass(Class<?> theClass) throws IllegalStateException{}
    public void setMapOutputValueClass(Class<?> theClass) throws IllegalStateException{}

    public void setPartitionerClass(Class<? extends Partitioner> cls) throws IllegalStateException
    public void setCombinerClass(Class<? extends Reducer> cls) throws IllegalStateException
    public void setCombinerKeyGroupingComparatorClass(Class<? extends RawComparator> cls) throws IllegalStateException {}
    public void setSortComparatorClass(Class<? extends RawComparator> cls) throws IllegalStateException //定义Map结果中key的比较规则
    public void setGroupingComparatorClass(Class<? extends RawComparator> cls) throws IllegalStateException

    public void setPriority(JobPriority priority) throws IOException, InterruptedException{}
    public void setNumReduceTasks(int tasks) throws IllegalStateException{}

    public void setJar(String jar){} //Set the job jar

    public void setJobName(String name) throws IllegalStateException{}

    //提交任务
    public void submit() throws IOException, InterruptedException, ClassNotFoundException{}
    public boolean waitForCompletion(boolean verbose) throws IOException, InterruptedException, ClassNotFoundException {}

    //条件判断
    public boolean isComplete() throws IOException {}
    public boolean isSuccessful() throws IOException {}
}

(2)org.apache.hadoop.mapreduce.lib.input.FileInputFormat

public abstract class FileInputFormat<K,V> extends InputFormat<K,V> {
    public static void setInputPaths(Job job, String commaSeparatedPaths) throws IOException {}
    public static void setInputPaths(Job job, Path... inputPaths) throws IOException {}
    public static void addInputPaths(Job job, String commaSeparatedPaths) throws IOException {}
    public static void addInputPath(Job job, Path path) throws IOException {}

    public static Path[] getInputPaths(JobContext context) {}
}

(3)org.apache.hadoop.mapreduce.lib.output.FileOutputFormat

public abstract class FileOutputFormat<K,V> extends OutputFormat<K,V> {
    public static void setOutputPath(Job job, Path outputDir) {}
    public static Path getOutputPath(JobContext job) {}
}

6.8 WordCount示例

(1)Mapper的实现类

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class MyMapper extends Mapper<Object, Text, Text, IntWritable> {
    private final IntWritable one = new IntWritable(1);
    private Text word = new Text();

    public void map(Object ikey, Text ivalue, Context context) throws IOException, InterruptedException {
        String[] words = ivalue.toString().split(" ");
        for (String item : words) {
            word.set(item);
            context.write(word, one);
        }
    }
}

(2)Reducer的实现类

import java.io.IOException;

import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.Reducer;

public class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
    public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
        // process values
        int count = 0;
        for (IntWritable val : values) {
            count += val.get();
        }

        context.write(key, new IntWritable(count));
    }

}

(3)Driver的实现类

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class MyDriver {

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "JobName");
        job.setJarByClass(MyDriver.class);
        job.setMapperClass(MyMapper.class);

        job.setReducerClass(MyReducer.class);

        // TODO: specify output types
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        // TODO: specify input and output DIRECTORIES (not files)
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        boolean res = job.waitForCompletion(true);
        System.exit(res ? 0 : 1);
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值