大数据技术Hadoop(MapReduce&Yarn)详细又精简的总结(案例、代码、图片)齐全

MapReduce

案例所需文件在文章末尾链接

第1章 MapReduce概述

1.1MapReduce定义

MapReduce是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架

1.3 MapReduce核心思想

在这里插入图片描述

1.6 常用数据序列化类型

Java类型Hadoop Writable****类型
BooleanBooleanWritable
ByteByteWritable
IntegerIntWritable
FloatFloatWritable
LongLongWritable
DoubleDoubleWritable
StringText
MapMapWritable
ArrayArrayWritable

1.8 WordCount案例实操

需求:统计文件中各单词出现的次数

//Mapper
package com.atguigu.mr.wordcount;

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

import java.io.IOException;

/*
    作用:实现MapTask中需要实现的业务逻辑代码

    说明
    1.MapTask会调用Mapper类。
    2.Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
        两组:
            第一组:
                KEYIN :读取数据时的偏移量的类型
                VALUEIN :读取的数据的类型(一行一行的数据)
             第二组:
                KEYOUT :写出的数据的key的类型
                VALUEOUT :写出的数据的Value的类型
 */
public class WCMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
    private Text outKey = new Text();//封装的key
    private IntWritable outValue = new IntWritable();//封装的value
    /*
     * 作用:实现MapTask中需要实现的业务逻辑代码
     * 说明:
     *      1.map方法是被MapTask去调用(不是我们调用的)
     *      2.map方法被循环调用每调用一次传一行数据
     * @param key 读取的数据的偏移量
     * @param value 读取的数据(一行一行的数据)
     * @param context 上下文(在这用来将数据写出去)
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //调用父类中的方法--不要写!!!!!!!!!!!!
        //super.map(key, value, context);‘
        //1.切割数据
        //1.1将value转成String
        String line = value.toString();
        //1.2切割数据
        String[] words = line.split(" ");

        //2.封装k,v
        //2.1遍历数组
        for(String word : words){
            //2.2封装key
            outKey.set(word);
            //2.3封装value
            outValue.set(1);

            //3.将K,V写出去 --注意:一定不要写到for循环外(在这个逻辑下)
            context.write(outKey,outValue);
        }
    }
}
//Reducer
package com.atguigu.mr.wordcount;

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

import java.io.IOException;

/*
    作用 :实现ReduceTask需要实现的业务逻辑代码

    说明:
        1.RedcuTask会调用Reducer这个类
        2. Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
            两组:
                第一组
                    KEYIN :读取数据时的key的类型(Mapper输出的key的类型)
                    VALUEIN :读取数据时的value的类型(Mapper输出的value的类型)
                第二组
                    KEYOUT :写出数据时的key的类型(在这就是单词的类型)
                    VALUEOUT :写出数据时的value的类型(在这就是单词数量的类型)
 */
public class WCReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
    private IntWritable outValue = new IntWritable();//封装的value
    /*
     * 作用 :实现ReduceTask需要实现的业务逻辑代码
     * 说明:
     *      1.reduce方法是ReduceTask调用的(在这就理解成是框架调用的)
     *      2.reduce方法在被循环调用,每调用一次传一组数据
     * @param key 读取的key的值
     * @param values 读取的所有的value
     * @param context 上下文 在这用来将k,v写出去
     * @throws IOException
     * @throws InterruptedException
     *
     * aaa  1
     * aaa  1
     * aaa  1
     */
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
        int sum = 0;//所有value的和
        //1.遍历所有的value
        for (IntWritable value : values) {
            //2.将所有的value累加
            //2.1将IntWritable转换成int
            int v = value.get();
            sum += v;
        }
        //3.封装k,v
        outValue.set(sum);
        //4.写出K,V
        context.write(key,outValue);
    }
}
1.本地模式Driver
//Driver
package com.atguigu.mr.wordcount;

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;

import java.io.IOException;


/*
   程序的入口

   大家常犯错误:
        1.在map方法和reduce方法中调用了父类的方法
        2.在给Job对象的属性赋值时
                两次都是setMapOutputKey 或 setOutputKey
        3.在给Job对象的属性赋值时
                k,v的类型导错包
        4.如果是3.1的同学排除代码外 报错可以尝试换成3.0(记住把c盘中hadoop.dll,winutils.exe也换掉)

 */
public class WCDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        //1.创建Job对象
        Configuration conf = new Configuration();//可以在该对象中做一些配置
        Job job = Job.getInstance(conf);
        //2.给Job对象的属性赋值
        //2.1设置Jar加载路径(如果是本地模式,可以不设置)
        job.setJarByClass(WCDriver.class);
        //2.2设置Mapper和Reducer类
        job.setMapperClass(WCMapper.class);
        job.setReducerClass(WCReducer.class);
        //2.3设置Mapper输出的k,v的类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);
        //2.4设置最终输出的K,V的类型(在这是Reducer输出的K,V类型)
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        //2.5设置数据的输入和输出路径
        FileInputFormat.setInputPaths(job,new Path("D:\\io\\input"));
        //注意:输出路径不能存在!!!!!!!!!!!!
        FileOutputFormat.setOutputPath(job,new Path("D:\\io\\output"));

        //3.提交Job对象(执行Job)
        /*
            参数 :是否打印进度
            返回值 :如果是true则表示job执行成功
         */
        boolean b = job.waitForCompletion(true);
        //JVM是否是正常退出 : 0正常退出,1非正常退出
        System.exit(b ? 0 : 1);
    }
}
2.集群模式Driver2
/*
1.将Driver2打包后在集群上运行
2.hadoop jar xxx.jar com.dai.mr.wordcount.WCDriver2    /input  /output
*/
package com.atguigu.mr.wordcount;

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;

import java.io.IOException;


/*
将MR打包在集群上运行
hadoop jar  xxx.jar  com.atguigu.mr.wordcount.WCDriver2  参数1  参数2

 */
public class WCDriver2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);
        job.setJarByClass(WCDriver2.class);
        job.setMapperClass(WCMapper.class);
        job.setReducerClass(WCReducer.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);)
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        
        
        //2.5设置数据的输入和输出路径
        FileInputFormat.setInputPaths(job,new Path(args[0]));
        //注意:输出路径不能存在!!!!!!!!!!!!
        FileOutputFormat.setOutputPath(job,new Path(args[1]));

 
        boolean b = job.waitForCompletion(true);
        System.exit(b ? 0 : 1);
    }
}

3.本地向集群提交任务模式Driver3

一般用于调试

/*
在本地向集群提交Job
1.配置内容
2.打包
3.设置Jar包的路径
4.将job.setJarByClass(WCDriver3.class);注释掉
5.在editconfiguration中配置
    VM Options :-DHADOOP_USER_NAME=atguigu
    Program Argument :hdfs://hadoop102:9820/input hdfs://hadoop102:9820/output2
 */
package com.dai.mr.wordcount;

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;

import java.io.IOException;

/*
在本地向集群提交Job
1.配置内容
2.打包
3.设置Jar包的路径:将jar包放在设置的路径,类似于设置input路径
4.将job.setJarByClass(WCDriver3.class);注释掉
5.在editconfiguration中配置
    VM Options :-DHADOOP_USER_NAME=atguigu
    Program Argument :hdfs://hadoop102:9820/input                                      	     hdfs://hadoop102:9820/output2
 */
public class WCDriver3 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

        Configuration conf = new Configuration();

        /*1.配置内容*/
        //设置在集群运行的相关参数-设置HDFS,NAMENODE的地址
        conf.set("fs.defaultFS", "hdfs://hadoop102:9820");
        //指定MR运行在Yarn上
        conf.set("mapreduce.framework.name","yarn");
        //指定MR可以在远程集群运行
        conf.set(
                "mapreduce.app-submission.cross-platform","true");
        //指定yarn resourcemanager的位置
        conf.set("yarn.resourcemanager.hostname", "hadoop103");



        Job job = Job.getInstance(conf);

        //job.setJarByClass(WCDriver3.class);
        /*3.设置jar包路径*/
        //设置jar包的路径
        job.setJar("G:\\0521\\06-hadoop\\3.代码\\MRDemo\\target\\MRDemo-1.0-SNAPSHOT.jar");


        job.setMapperClass(WCMapper.class);
        job.setReducerClass(WCReducer.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        FileInputFormat.setInputPaths(job,new Path(args[0]));
        FileOutputFormat.setOutputPath(job,new Path(args[1]));
        boolean b = job.waitForCompletion(true);
        System.exit(b ? 0 : 1);
    }
}

第2章 Hadoop序列化

2.1什么是序列化

序列化就是内存中的对象,转换成字节序列以便于存储到磁盘(持久化)

反序列化就是将字节序列或者是磁盘持久化的数据,转换成内存中的对象

2.2 自定义bean对象实现序列化接口(Writable)

具体实现bean对象序列化步骤如下7步

(1)必须实现Writable接口

(2)反序列化时,需要反射调用空参构造函数,所以必须有空参构造

(3)重写序列化方法

@Override
public void write(DataOutput out) throws IOException {
	out.writeLong(upFlow);
	out.writeLong(downFlow);
	out.writeLong(sumFlow);
}

(4)重写反序列化方法

@Override
public void readFields(DataInput in) throws IOException {
	upFlow = in.readLong();
	downFlow = in.readLong();
	sumFlow = in.readLong();
}

(5)注意反序列化的顺序和序列化的顺序完全一致

(6)要想把结果显示在文件中,需要重写toString(),可用”\t”分开,方便后续用

(7)如果需要将自定义的bean放在key中传输,则还需要实现Comparable接口,因为MapReduce框中的Shuffle过程要求对key必须能排序

2.3 序列化案例实操

//FlowBean
package com.atguigu.mr.writable2;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Writable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;


public class FlowBean implements Writable {
    private long upFlow;
    private long downFlow;
    private long sumFlow;

    public FlowBean(){

    }

    public FlowBean(long upFlow, long downFlow) {
        this.upFlow = upFlow;
        this.downFlow = downFlow;
        this.sumFlow = upFlow + downFlow;
    }

    /*
            当序列化时调用此方法
         */
    public void write(DataOutput out) throws IOException {
        out.writeLong(upFlow);
        out.writeLong(downFlow);
        out.writeLong(sumFlow);
    }

    /*
        当反序列化时调用此方法
        注意:反序列化时读取数据的顺序要和序列化时写数据的顺序保持一致
     */
    public void readFields(DataInput in) throws IOException {
        upFlow = in.readLong();
        downFlow = in.readLong();
        sumFlow = in.readLong();
    }

    @Override
    public String toString() {
        return upFlow + " " + downFlow + " " + sumFlow;
    }
}


//Mapper
package com.atguigu.mr.writable2;

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

import java.io.IOException;

/*
    作用:实现MapTask中需要实现的业务逻辑代码

    说明
    1.MapTask会调用Mapper类。
    2.Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
        两组:
            第一组:
                KEYIN :读取数据时的偏移量的类型
                VALUEIN :读取的数据的类型(一行一行的数据)
             第二组:
                KEYOUT :写出的数据的key的类型(手机号)
                VALUEOUT :写出的数据的Value的类型(FlowBean)
 */
public class FlowMapper extends Mapper<LongWritable, Text,Text,FlowBean> {
    /*
     * 作用:实现MapTask中需要实现的业务逻辑代码
     * 说明:
     *      1.map方法是被MapTask去调用(不是我们调用的)
     *      2.map方法被循环调用每调用一次传一行数据
     * @param key 读取的数据的偏移量
     * @param value 读取的数据(一行一行的数据)
     * @param context 上下文(在这用来将数据写出去)
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //1.将value转成字符串
        String line = value.toString();
        //2.将数据切割
        String[] flowInfo = line.split("\t");
        //3.封装k,v
        Text outkey = new Text(flowInfo[1]);
        FlowBean outValue = new FlowBean(Long.parseLong(flowInfo[flowInfo.length - 3]),
                Long.parseLong(flowInfo[flowInfo.length - 2]));
        //4.写出K,V
        context.write(outkey,outValue);
    }
}

//Reducer
package com.atguigu.mr.writable2;

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

import java.io.IOException;

/*
    作用 :实现ReduceTask需要实现的业务逻辑代码

    说明:
        1.RedcuTask会调用Reducer这个类
        2. Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
            两组:
                第一组
                    KEYIN :读取数据时的key的类型(Mapper输出的key的类型)
                    VALUEIN :读取数据时的value的类型(Mapper输出的value的类型)
                第二组
                    KEYOUT :写出数据时的key的类型(在这就是单词的类型)
                    VALUEOUT :写出数据时的value的类型(在这就是单词数量的类型)
 */
public class FLowReducer extends Reducer <Text,FlowBean,Text,FlowBean>{
/*
     * 作用 :实现ReduceTask需要实现的业务逻辑代码
     * 说明:
     *      1.reduce方法是ReduceTask调用的(在这就理解成是框架调用的)
     *      2.reduce方法在被循环调用,每调用一次传一组数据
     * @param key 读取的key的值 --- 手机号
     * @param values 读取的所有的value --- FlowBean
     * @param context 上下文 在这用来将k,v写出去
     * @throws IOException
     * @throws InterruptedException
     *
     *   key            value
     *  手机号1,对象(upflow,downflow,sumflow)
        手机号1,对象 (upflow,downflow,sumflow)

     */
    @Override
    protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException {
        int sumUpFlow = 0;//总上行流量
        int sumDownFLow = 0;//总下行流量
        //1.遍历所有的value
        for (FlowBean value : values) {
            //2.将上行流量,下行流量进行累加
            sumUpFlow += value.getUpFlow();
            sumDownFLow += value.getDownFlow();
        }
        //3.封装k,v
        FlowBean outValue = new FlowBean(sumUpFlow, sumDownFLow);
        //4.写出K,V
        context.write(key,outValue);
    }
}


//Driver
package com.atguigu.mr.writable2;

import com.atguigu.mr.wordcount.WCDriver;
import com.atguigu.mr.wordcount.WCMapper;
import com.atguigu.mr.wordcount.WCReducer;
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;

import java.io.IOException;

public class FlowDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        //1.创建Job对象
        Job job = Job.getInstance(new Configuration());

        //2.给Job对象的属性赋值
        //2.1设置Jar加载路径(如果是本地模式,可以不设置)
        job.setJarByClass(FlowDriver.class);
        //2.2设置Mapper和Reducer类
        job.setMapperClass(FlowMapper.class);
        job.setReducerClass(FLowReducer.class);
        //2.3设置Mapper输出的k,v的类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(FlowBean.class);
        //2.4设置最终输出的K,V的类型(在这是Reducer输出的K,V类型)
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);
        //2.5设置数据的输入和输出路径
        FileInputFormat.setInputPaths(job,new Path("D:\\io\\input2"));
        //注意:输出路径不能存在!!!!!!!!!!!!
        FileOutputFormat.setOutputPath(job,new Path("D:\\io\\output2"));

        //3.提交Job对象(执行Job)
        /*
            参数 :是否打印进度
            返回值 :如果是true则表示job执行成功
         */
        job.waitForCompletion(true);
    }
}

第3章 MapReduce框架原理

3.1 InputFormat数据输入

在这里插入图片描述

3.1.1同时运行几个maptask的决定机制

在这里插入图片描述

3.1.2 Job提交流程源码和切片源码详解
//1提交Job
job.waitForCompletion(true);
	//1.1
	submit();

	//1.1.1建立连接
	connect();	
		//创建提交Job的代理
		new Cluster(getConfiguration());
			//判断是本地yarn还是远程(如果是本地那么创建LocalJobRunner,如果是集群创建YarnRunner)
			initialize(jobTrackAddr, conf); 

	//1.1.2 提交job
	submitter.submitJobInternal(Job.this, cluster)
		//创建给集群提交数据的Stag路径(本地提交就在MR程序所在的盘符.如果是集群就在HDFS上)
		Path jobStagingArea = JobSubmissionFiles.getStagingDir(cluster, conf);

		//获取jobid ,并创建Job路径
		JobID jobId = submitClient.getNewJobID();

		//拷贝jar包到集群
		copyAndConfigureFiles(job, submitJobDir);	
			//如果是向集群提交,那么会上传jar包
			rUploader.uploadFiles(job, jobSubmitDir);

		//计算切片,生成切片规划文件
		writeSplits(job, submitJobDir);
			maps = writeNewSplits(job, jobSubmitDir);
				//调用的InputFormat中的getSplits方法用来生成切片信息
				input.getSplits(job);

		//向Stag路径写XML配置文件
		writeConf(conf, submitJobFile);
			//将配置信息写到指定的staging路径   job.xml
			conf.writeXml(out);

		//提交Job,返回提交状态(submitClient如果是本地那就是LocalJobRunner,否则就是YarnRunner)
		status = submitClient.submitJob(jobId, submitJobDir.toString(), job.getCredentials());


3.1.3 FileInputFormat切片机制

作用:

1.InputFormat中的getSplits方法实现对信息切片

​ FileInputFormat重写了getSplits方法

2.InputFormat中的RecordReader实现数据的读取

​ TextInputFormat重写了RecordReader方法,实现对数据一行一行的读取

Input --> InputFormat(切片 一行一行读信息) --> Mapper --->Shuffle ---> Reducer ---> OutputFormat --->Output

1.InputFormat的继承树
	|-----InputFormat(抽象类)
		|------FileInputFormat(抽象类)
			|-------TextInputFormat(默认使用的InputFormat)
			

2.InputFormat(抽象类)
 /*
	生成切片信息
 */
 public abstract List<InputSplit> getSplits(JobContext context) throws IOException, InterruptedException;
							   
/*
	创建RecordReader对象,该对象是用来读取数据的。
*/ 
 public abstract RecordReader<K,V> createRecordReader(
     InputSplit split,TaskAttemptContext context) 
												 
												 					 
3.FileInputFormat(抽象类)
	该类中重写了getSplits方法。
	

4.TextInputFormat(默认使用的InputFormat)
/*
	1.重写了 createRecordReader方法。
	2.在createRecordReader方法中创建了LineRecordReader,该对象是用来读取数据的,一行一行的读取数据。
	3.LineRecordReader是RecordReader的子类。
*/	
	  @Override
	  public RecordReader<LongWritable, Text> 
		createRecordReader(InputSplit split,
						   TaskAttemptContext context) {
	   
		return new LineRecordReader(recordDelimiterBytes);
	  }

3.2 MapReduce工作流程

1.InputFormat进行切片后将job提交过程中生产的配置传到HDFS

2.客户端向Yarn提交job

3.Yarn中的RM分配appmaster,appmaster根据提交的信息的大小向RM申请多少资源

4.RM分配Container执行MapTask

5.InputFormat中的RecordReader将文本信息一行一行的读入Mapper中

6.Mapper根据读入的信息执行Map方法

在这里插入图片描述

3.3 Shuffle机制

在这里插入图片描述
在这里插入图片描述

3.3.2 Partition分区
1.如果有多个ReduceTask时的默认分区:

public class HashPartitioner<K, V> extends Partitioner<K, V> {

  /** 
	作用 :返回分区号
	K : map输出的key
	V : map输出的value
	numReduceTasks : ReduceTask的数量
  */
  public int getPartition(K key, V value,int numReduceTasks) {
						  
	/*
		分区号 = key.hashCode()  % numReduceTasks
		
		key.hashCode() & Integer.MAX_VALUE : 作用是用来保证结果为正数
	
	*/
    return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
  }


在这里插入图片描述

2.如果只有1ReduceTask时的默认分区:
		partitioner = new org.apache.hadoop.mapreduce.Partitioner<K,V>() {
          @Override
          public int getPartition(K key, V value, int numPartitions) {
            return partitions - 1; //0
          }
        };


在这里插入图片描述

3.3.3 Partition分区案例实操

1.设置ReducerTask数量

job.setNumReduceTasks(5);

2.设置自定义分区类

job.setPartitionerClass(MyPartitoner.class);

3.自定义分区类

package com.atguigu.mr.partition2;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;

/*
    自定义分区类:
    1.自定义类并继承Partitioner
           Partitioner<KEY, VALUE> :
                KEY :mapper写出的key的类型
                VALUE :mapper写出的value的类型

     2.重写getPartition方法
 */
public class MyPartitioner extends Partitioner<Text,FlowBean> {
    /**
     * 返回分区号
     * @param text :mapper输出的key --手机号
     * @param flowBean mapper输出的value - flowBean
     * @param numPartitions reduceTask的数量
     * @return
     */
    public int getPartition(Text text, FlowBean flowBean, int numPartitions) {
        /*
            需求 :手机号136、137、138、139开头都分别放到一个独立的4个文件中,其他开头的放到一个文件中。
         */
        String phoneNumber = text.toString();
        //2.匹配手机号
        if(phoneNumber.startsWith("136")){
            return 0;
        }else if(phoneNumber.startsWith("137")){
            return 1;
        }else if(phoneNumber.startsWith("138")){
            return 2;
        }else if(phoneNumber.startsWith("139")){
            return 3;
        }else{
            return 4;
        }
    }
}

3.3.4 WritableComparable排序

自定义排序WritableComparable原理分析

bean对象做为key传输,需要实现WritableComparable接口重写compareTo方法,就可以实现排序。

要记得加泛型

3.3.5WritableComparable排序案例实操(全排序)

需求:根据案例2.3产生的结果再次对总流量进行排序

//FlowBean
public class FlowBean implements WritableComparable<FlowBean>//要记得加泛型
  
 /*
      	在该方法中指定按照哪个属性进行排序
 */
    public int compareTo(FlowBean o) {
        //按照总流量进行排序
        return Long.compare(this.sumFlow,o.sumFlow);
    }

//Mapper
package com.atguigu.mr.comparable2;

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

import java.io.IOException;

/*
    注意:  一定要让FlowBean作为key,因为只能对key进行排序。
 */
public class CPMapper extends Mapper<LongWritable,Text,FlowBean, Text> {
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //1.将value转成String
        String ling = value.toString();
        //2.切割
        String[] phoneInfo = ling.split("\t");
        //3.封装K,V
        FlowBean outKey = new FlowBean(Long.parseLong(phoneInfo[1]),
                Long.parseLong(phoneInfo[2]), Long.parseLong(phoneInfo[3]));
        Text outValue = new Text(phoneInfo[0]);
        //4.将k,v写出去
        context.write(outKey,outValue);
    }
}

//Reducer
package com.atguigu.mr.comparable2;

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

import java.io.IOException;

/*
    只是将K,V进行调换
    注意 : 一定要将泛型调换顺序
    因为value是唯一的
 */
public class CPReducer extends Reducer<FlowBean, Text,Text,FlowBean> {
    @Override
    protected void reduce(FlowBean key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
        //遍历所有的value
        for (Text outKey : values) {
            //将K,V写出去(原来的K变成V,原料来的V变成K)
            context.write(outKey,key);
        }
    }
}

//Driver

package com.atguigu.mr.comparable2;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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;

import java.io.IOException;

public class CPDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        //1.创建Job对象
        Job job = Job.getInstance(new Configuration());

        //2.给Job对象赋值
        job.setMapperClass(CPMapper.class);
        job.setReducerClass(CPReducer.class);

        job.setMapOutputKeyClass(FlowBean.class);
        job.setMapOutputValueClass(Text.class);

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);

        FileInputFormat.setInputPaths(job,new Path("D:\\io\\input10"));
        FileOutputFormat.setOutputPath(job,new Path("D:\\io\\output1111"));

        //3.提交Job
        job.waitForCompletion(true);
    }
}

3.3.6WritableComparable排序案例实操(区内排序)

需求:要求每个省份手机号输出的文件中按照总流量内部排序,基于前一个需求,增加自定义分区类,分区按照省份手机号设置

//Driver

package com.atguigu.mr.comparable3;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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;

import java.io.IOException;

public class CPDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Job job = Job.getInstance(new Configuration());

        //1.设置使用自定义分区类
        job.setPartitionerClass(MyPartitioner.class);
        //2.设置ReduceTask的数量
        job.setNumReduceTasks(5);

        job.setMapperClass(CPMapper.class);
        job.setReducerClass(CPReducer.class);
        job.setMapOutputKeyClass(FlowBean.class);
        job.setMapOutputValueClass(Text.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);
        FileInputFormat.setInputPaths(job,new Path("D:\\io\\input10"));
        FileOutputFormat.setOutputPath(job,new Path("D:\\io\\output11"));
        job.waitForCompletion(true);
    }
}


//MyPartitioner

package com.atguigu.mr.comparable3;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;


/*
    自定义分区类 :
            key : mapper输出的key
            value : mappper输出的value
 */
public class MyPartitioner extends Partitioner<FlowBean, Text> {
    @Override
    public int getPartition(FlowBean flowBean, Text text, int numPartitions) {
        //1.将text转成String
        String phoneNumber = text.toString();
        //2.匹配手机号
        if(phoneNumber.startsWith("136")){
            return 0;
        }else if(phoneNumber.startsWith("137")){
            return 1;
        }else if(phoneNumber.startsWith("138")){
            return 2;
        }else if(phoneNumber.startsWith("139")){
            return 3;
        }else{
            return 4;
        }
    }
}


3.6 OutputFormat数据输出

3.6.1 OutputFormat接口实现类

作用:

1.OutputFormat中的checkOutputSpecs检查输出路径

​ FileOutputFormat重写了checkOutputSpecs方法,实现

​ 1.检查输出路径是否存在

​ 2.输出路径是否设置

2.OutputFormat中的RecordWriter写数据

​ TextOutputForam重写了RecordWriter方法,实现一行一行的将数据写出

一 查看OutputFormat的继承树
	|-----OutputFormat(抽象类)
		|------FileOutputFormat(抽象类)
			|-----TextOutputForamt(默认使用的OutputFormat类)
			

			
二 OutputFormat(抽象类)
	/*
		返回一个RecordWriter对象,该对象是真正用来写数据的对象。
	*/
  public abstract RecordWriter<K, V> getRecordWriter(TaskAttemptContext context
                    ) throws IOException, InterruptedException;
					
	
	/*
		该方法用来检查输出的一些设置(1.输出的路径是否存在 2.是否设置了输出路径)
	*/
	public abstract void checkOutputSpecs(JobContext context
                                        ) throws IOException, 
                                                 InterruptedException;
												 
三 	FileOutputFormat(抽象类)			
		该类中重写了checkOutputSpecs用来检查
			1.输出的路径是否存在 2.是否设置了输出路径
			
			
			
四 	TextOutputForamt(默认使用的OutputFormat类)	
		  1.重写了getRecordWriter方法,返回了LineRecordWriter对象。
		  2.LineRecordWriter就是真正用来写数据的对象,一行一行的写。
		  3.LineRecordWriter是RecordWriter的子类。
		  
 public RecordWriter<K, V> getRecordWriter(TaskAttemptContext job
                         	) throws IOException, InterruptedException {
	
	 return new LineRecordWriter<>(fileOut, keyValueSeparator);

		}										

3.6.2 自定义OutputFormat

1.为了控制最终文件的输出路径和输出格式,可以自定义OutputFormat

2.自定义OutputFormat步骤

​ (1)自定义一个类MyOutputFormat继承FileOutputFormat

​ (2)改写RecordWriter,具体改写输出数据的方法write()

3.6.3 自定义OutputFormat案例实操

1)需求

过滤输入的log日志,包含atguigu的网站输出到e:/atguigu.log,不包含atguigu的网站输出到e:/other.log。

2)步骤

​ 1.在Driver中配置自定义OutputFormat

//自定义OutputFormat,如果不自定义使用默认FileOutputFormat
job.setOutputFormatClass(MyOutputFormat.class);

​ 2.自定义MyOutputFormat类

​ 3.在MyOutputForma中自定义MyRecordWriter类

// 1.在Driver中配置自定义OutputFormat
package com.atguigu.mr.output;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
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;

import java.io.IOException;

public class OutputDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

        Job job = Job.getInstance(new Configuration());

        job.setOutputKeyClass(LongWritable.class);
        job.setOutputValueClass(Text.class);

        
        //设置自定义的OutputFormat类 ---- 如果不指定使用TextOutputFormat
        job.setOutputFormatClass(MyOutputFormat.class);
        

        FileInputFormat.setInputPaths(job,new Path("D:\\io\\input7"));
        FileOutputFormat.setOutputPath(job,new Path("D:\\io\\output"));

        job.waitForCompletion(true);
    }
}


//2.自定义MyOutputFormat类

package com.atguigu.mr.output;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.OutputFormat;
import org.apache.hadoop.mapreduce.RecordWriter;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;

/*
    自定义OutputFormat类
    1.自定义一个类并继承FileOutputFormat,因为继承此类只需要重写getRecordWriter方法。
        而此方法正是我们所需要的。
    2.FileOutputFormat泛型的类型是Reducer输出的K,V类型。
        没有Reducer是Mapper输出的K,V
        没有Mapper也没有那么是InputFormat输出的K,V类型
 */
public class MyOutputFormat extends FileOutputFormat<LongWritable, Text> {
    /*
        返回RecordWriter对象。
     */
    @Override
    public RecordWriter<LongWritable, Text> getRecordWriter(TaskAttemptContext job)
            throws IOException, InterruptedException {

        
        //通过构造器传递Job
        return new MyRecordWriter(job);
    }
}


package com.atguigu.mr.output;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.RecordWriter;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;


/*
    自定义RecordWriter类。
 */
public class MyRecordWriter extends RecordWriter<LongWritable, Text> {

    private FSDataOutputStream atguigu;
    private FSDataOutputStream other;

    /*
        创建流
     */
    
   /*
   		TaskAttemptContext job
   		通过构造器传参,因为FileSystem.get(configuration)中的configuration在Driver中已经配置了,		只能进行调用保证在运行环境中的configuration保持一致
   */
    
    public MyRecordWriter(TaskAttemptContext job){
        try {
            //1.创建流
            FileSystem fs = FileSystem.get(job.getConfiguration());
            //2.创建流 -- 输出流
            //2.1获取输出路径
            Path outputPath = FileOutputFormat.getOutputPath(job);
            //2.2创建流
            
            //将输出路径进行拼接 outputPath/atguigu.txt,因为Driver中配置了输出路径
            atguigu = fs.create(new Path(outputPath, "atguigu.txt"));
            other = fs.create(new Path(outputPath, "other.txt"));
            
        }catch (Exception e){
            //终止程序运行
            throw new RuntimeException(e.getMessage()); //将编译时异常转成运行异常
        }

    }

    /**
     * 写数据
     * @param key 偏移量
     * @param value 一行一行的数据。该方法在被循环调用,每调用一次传一行数据
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    public void write(LongWritable key, Text value) throws IOException, InterruptedException {
        //1.先判断网址有没有包含atguigu
        String address = value.toString() + "\n";
        if (address.contains("atguigu")){
            
            //写到atguigu.txt
            atguigu.write(address.getBytes());
            
        }else{
            
            //写到other.txt
            other.write(address.getBytes());
            
        }
    }

    /*
        在该方法中用来关闭资源 --- 当写操作结束以后才会调用此方法(框架调用的)
     */
    @Override
    public void close(TaskAttemptContext context) throws IOException, InterruptedException {
        IOUtils.closeStream(atguigu);
        IOUtils.closeStream(other);
    }
}

3.7 Join多种应用

3.7.2 Reduce Join案例实操

​ 需求:4-4和4-5合成4-6

​ 表4-4 订单数据表t_order

idpidamount
1001011
1002022
1003033
1004014
1005025
1006036

​ 表4-5 商品信息表t_product

pidpname
01小米
02华为
03格力

​ 表4-6 最终数据形式

idpnameamount
1001小米1
1004小米4
1002华为2
1005华为5
1003格力3
1006格力6
package com.atguigu.mr.reducejoin;

import org.apache.hadoop.io.WritableComparable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

/*
    JavaBean
 */
public class OrderBean implements WritableComparable<OrderBean> {
    private String id;
    private String pid;
    private String amount;
    private String pname;

    public OrderBean(){

    }

    public OrderBean(String id, String pid, String amount, String pname) {
        this.id = id;
        this.pid = pid;
        this.amount = amount;
        this.pname = pname;
    }
    /*
        排序 : 先按照pid排序,pid相同再按照pname排序
     ""   	1		""       小米
    1001	1	    1       ""
    1004	1	    4       ""

     */
    @Override
    public int compareTo(OrderBean o) {
        int pidValue = this.pid.compareTo(o.pid);
        if (pidValue == 0){//说明pid相同,再按照pname排序
            return -this.pname.compareTo(o.pname);
        }
        return pidValue;
    }

    /*
        序列化
     */
    @Override
    public void write(DataOutput out) throws IOException {
        out.writeUTF(id);
        out.writeUTF(pid);
        out.writeUTF(pname);
        out.writeUTF(amount);
    }

    /*
        反序列化
     */
    @Override
    public void readFields(DataInput in) throws IOException {
        id = in.readUTF();
        pid = in.readUTF();
        pname = in.readUTF();
        amount = in.readUTF();
    }

    @Override
    public String toString() {
        return id + " " + pid + " " + amount + " " + pname;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getPid() {
        return pid;
    }

    public void setPid(String pid) {
        this.pid = pid;
    }

    public String getAmount() {
        return amount;
    }

    public void setAmount(String amount) {
        this.amount = amount;
    }

    public String getPname() {
        return pname;
    }

    public void setPname(String pname) {
        this.pname = pname;
    }


}


package com.atguigu.mr.reducejoin;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;

import java.io.IOException;

public class RJMapper extends Mapper<LongWritable, Text,OrderBean, NullWritable> {
    private String fileName;

    /*
        该方法在任务开始的时候只执行一次,在map方法调用前执行。

        初始化
     */
    @Override
    protected void setup(Context context) throws IOException, InterruptedException {
       //1.获取切片信息
        FileSplit fs = (FileSplit) context.getInputSplit();
        // 2.通过切片获取文件名
        fileName = fs.getPath().getName();
    }

    /*
        该方法在任务结束的时候只执行一次,在map方法调用后执行。

        关闭资源
     */
    @Override
    protected void cleanup(Context context) throws IOException, InterruptedException {

    }

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String line = value.toString();
        //切割数据
        String[] info = line.split("\t");
        //创建对象
        OrderBean bean = new OrderBean();
        //封装
        if("order.txt".equals(fileName)){
            bean.setId(info[0]);
            bean.setPid(info[1]);
            bean.setAmount(info[2]);
            //注意:一定要给Pname一个空串 "" ---- 因为排序的时候先按照pid排序再按照Pname排序
            bean.setPname("");
        }else if("pd.txt".equals(fileName)){
            bean.setPid(info[0]);
            bean.setPname(info[1]);
            bean.setId("");
            bean.setAmount("");
        }
        //写出K,V
        context.write(bean,NullWritable.get());
    }
}


//Reducer
package com.atguigu.mr.reducejoin;

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

import java.io.IOException;
import java.util.Iterator;

public class RJReducer extends Reducer<OrderBean, NullWritable,OrderBean,NullWritable> {
    /*
                key                      value
         null   1     null     小米        null
         1001   1     1       null        null
         1004   1     4        null        null
     */
    @Override
    protected void reduce(OrderBean key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
        //获取迭代器对象。
        Iterator<NullWritable> iterator = values.iterator();
        //1.获取该组数据中的第一行
        iterator.next();//指针下移并返回指针指向的数据
        //2.获取pname:小米
        String pname = key.getPname();
        //3.将剩余的所有的数据的pname全部进行替换
        while(iterator.hasNext()){//是否有下一条数据
            iterator.next();//指针下移并返回指针指向的数据
            //替换pname值
            key.setPname(pname);
            //4.将k,v写出去
            context.write(key,NullWritable.get());
        }

    }
}

//Driver
package com.atguigu.mr.reducejoin;

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

import java.io.IOException;

public class RJDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Job job = Job.getInstance(new Configuration());
        //设置自定义分组类
        job.setGroupingComparatorClass(MyCompartor.class);
        job.setMapperClass(RJMapper.class);
        job.setReducerClass(RJReducer.class);

        job.setMapOutputKeyClass(OrderBean.class);
        job.setMapOutputValueClass(NullWritable.class);
        job.setOutputKeyClass(OrderBean.class);
        job.setOutputValueClass(NullWritable.class);

        FileInputFormat.setInputPaths(job,
                new Path("D:\\io\\input16"));
        FileOutputFormat.setOutputPath(job,
                new Path("D:\\io\\output17"));

        job.waitForCompletion(true);
    }
}

3.9 数据清洗(ETL)

在运行核心业务MapReduce程序之前,往往要先对数据进行清洗,清理掉不符合用户要求的数据。清理的过程往往只需要运行Mapper程序,不需要运行Reduce程序

需求:

去除日志中字段个数小于等于11的日志(注意是字段) 如图是4个字段

在这里插入图片描述

//Mapper

package com.atguigu.mrfinal.ETL;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;

import java.io.IOException;

public class Mapper extends org.apache.hadoop.mapreduce.Mapper<LongWritable, Text,Text, NullWritable> {
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String line = value.toString();
        //切割后将切割的元素存入数组 
        String[] lineSize = line.split(" ");
        //判断数组的长度
        if(lineSize.length>11){
            //输出value,即切割后字段长度大于11的Text
            context.write(value,NullWritable.get());
        }
    }
}


//Driver
package com.atguigu.mrfinal.ETL;

import com.atguigu.mrfinal.writable.FMapper;
import com.atguigu.mrfinal.writable.FReducer;
import com.atguigu.mrfinal.writable.FlowBean;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
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;

import java.io.IOException;

public class FDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        //1.创建Job对象
        Job job = Job.getInstance(new Configuration());


        //2.给Job对象的属性赋值
        //2.1设置Jar加载路径(如果是本地模式,可以不设置)
        job.setJarByClass(FDriver.class);
        //2.2设置Mapper和Reducer类
        job.setMapperClass(Mapper.class);

        //2.3设置Mapper输出的k,v的类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(NullWritable.class);

        //2.5设置数据的输入和输出路径
        FileInputFormat.setInputPaths(job,new Path("D:\\io\\input"));
        //注意:输出路径不能存在!!!!!!!!!!!!
        FileOutputFormat.setOutputPath(job,new Path("D:\\io\\output"));

        //3.提交Job对象(执行Job)
        /*
            参数 :是否打印进度
            返回值 :如果是true则表示job执行成功
         */
        job.waitForCompletion(true);
    }
}

第4章 Yarn资源调度器

Yarn是一个资源调度平台,负责为运算程序提供服务器运算资源,相当于一个分布式的操作系统平台,而MapReduce等运算程序则相当于运行于操作系统之上的应用程序。

​ 分配资源给MR执行MR操作

4.1 Yarn基本架构

1.客户端向RM提交job

2.RM接到 Job后将job放入任务队列

3.NM去RM中领取任务创建Container,并产生MR AppMstr

4.MR AppMstr向RM申请资源执行MT

5.RM将任务放入队列

6.NM来领取RM中的任务创建Container,并执行MT

7.MR AppMstr将Map程序启动脚本发给执行MT的NM

8.MT执行完毕后,MR AppMstr向RM申请资源执行RT

9.Reducer向Map获取相应分区的数据后归并排序

在这里插入图片描述

NodeManager:管理单个节点上的资源

ResourceManager:管理所有的NodeManager

App Mstr:任务(MapTask,ReduceTask)的老大

​ 负责数据的切分:将数据进行实际的切分后给到MapTask

Container:MT和RT都是在Container中运行

4.2 Yarn工作机制

在这里插入图片描述

​ (1)YarnRunner向ResourceManager申请一个Application。

​ (2)RM将该应用程序的资源路径返回给YarnRunner。

​ (3)该程序将运行所需资源提交到HDFS上。

​ (4)程序资源提交完毕后,申请运行mrAppMaster。

​ (6)RM将用户的请求初始化成一个Task。

​ (7)其中一个NodeManager去领取到Task任务。

​ (8)该NodeManager创建容器Container,并产生MRAppmaster。

​ (9)Container从HDFS上拷贝资源到本地。

​ (10)MRAppmaster向RM 申请运行MapTask资源。

​ (11)RM将运行MapTask任务分配给另外两个NodeManager,另两个NodeManager分别领取任务并创建容器。

​ (12)MR向两个接收到任务的NodeManager发送程序启动脚本,这两个NodeManager分别启动MapTask,MapTask对数据分区排序。

(13)MrAppMaster等待所有MapTask运行完毕后,向RM申请容器,运行ReduceTask。

​ (14)ReduceTask向MapTask获取相应分区的数据。

​ (15)程序运行完毕后,MR会向RM申请注销自己。

案例所需材料:
链接:https://pan.baidu.com/s/1jKsZAiHEFormIJJjRW_mBg
提取码:a2a2
–来自百度网盘超级会员V1的分享

参考资料:
尚硅谷大数据技术之Hadoop(MapReduce&Yarn)
(作者:尚硅谷大数据研发部)

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值