MapReduce

原理

yarn的调度过程看下面这篇博客
https://blog.csdn.net/qq_45335413/article/details/108186427
MapReduce的编程模型,看下面这篇博客
https://blog.csdn.net/qq_45335413/article/details/107722220

一、开发自己的WordCount程序

1、导入下面的jar包

$HADOOP_HOME/share/hadoop/common/*.jar
$HADOOP_HOME/share/hadoop/common/lib/*.jar
$HADOOP_HOME/share/hadoop/mapreduce/*.jar
$HADOOP_HOME/share/hadoop/mapreduce/lib/*.jar

2、分析分析WordCount数据处理的过程

在这里插入图片描述

3、编写代码三个类

3.1 WorldCountMap

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;

/**
 * @author 吴明辉
 * @date 2020/9/2-${TIEM}
 */
public class WorldCountMap extends Mapper<LongWritable, Text,Text, IntWritable> {

    @Override
    protected void map(LongWritable key1, Text value1, Context context) throws IOException, InterruptedException {
        /*
         * context代表Map的上下文
         * 上文:HDFS
         * 下文:Reducer
         */
        //拿到一条数据
        String data = value1.toString();
        //对数据进行分词操作,得到words数组
        String[] worlds = data.split(" ");
        //对words数组进行遍历,输出到reduce
        for (String world : worlds) {
            context.write(new Text(world),new IntWritable(1));
        }
    }
}

3.2 WorldCountReduce

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

/**
 * @author 吴明辉
 * @date 2020/9/2-${TIEM}
 */
public class WorldCountReduce extends Reducer<Text, IntWritable,Text,IntWritable> {
    @Override
    protected void reduce(Text key3, Iterable<IntWritable> values3, Context context) throws IOException, InterruptedException {
        //遍历数组的集合元素做一个累加
        int sum = 0;
        for (IntWritable i:
             values3) {
            sum = sum + i.get();

        }
        //输出结果
        context.write(key3,new IntWritable(sum));
    }
}

3.3WorldCountMain

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.fs.Path;
/**
 * @author 吴明辉
 * @date 2020/9/2-${TIEM}
 */
public class WorldCountMain {
    public static void main(String[] args) throws Exception {
        // 1、创建任务Job,并且指定任务的入口
        Job job = Job.getInstance(new Configuration());
        job.setJarByClass(WorldCountMain.class);

        // 2、指定任务的Map、和Map的输出类型<k2   v2>
        job.setMapperClass(WorldCountMap.class);
        job.setMapOutputKeyClass(Text.class); // k2 单词
        job.setMapOutputValueClass(IntWritable.class); //v2 记一次数

        // 3、指定任务的Reduce、和Reduce输出的类型<k4   v4>
        job.setReducerClass(WorldCountReduce.class);
        job.setOutputKeyClass(Text.class);  // k4
        job.setOutputValueClass(IntWritable.class); // v4

        // 4、指定任务的输入路径、和输出路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        // 5、执行任务
        job.waitForCompletion(true);
    }
}

注意事项:
1、输出路径必须不存在
2、虚拟内存给关闭
3、导入类不能导错

二、高级特性

1、序列化Writable

回顾:Java的序列化例子
https://blog.csdn.net/qq_45335413/article/details/108390866
同样道理,Hadoop序列化需要需要继承Writable接口。
思考:yarn在执行mapper和reduce的时候,是不是要进行6次的io操作,对象在io操作保存数据的时候,是不是要进行序列的操作,Hadoop中已经把所有的数据类型做了一个序列化,只需要继承Writable接口就行了 。

1.1 Employee类

public class Employee implements Writable{
	private int empno;
	private String ename;
	private String job;
	private int mgr;
	private String hiredate;
	private int sal;
	private int comm;
	private int deptno;
	
	@Override
	public String toString() {
		return "Employee [empno=" + empno + ", ename=" + ename + ", sal=" + sal + ", deptno=" + deptno + "]";
	}

	@Override
	public void readFields(DataInput input) throws IOException {
		// 反序列化:输入
		this.empno = input.readInt();
		this.ename = input.readUTF();
		this.job = input.readUTF();
		this.mgr = input.readInt();
		this.hiredate = input.readUTF();
		this.sal = input.readInt();
		this.comm = input.readInt();
		this.deptno = input.readInt();
	}
	
	//注意:序列化的顺序一定要和反序列化的顺序一致!!!!!
	
	@Override
	public void write(DataOutput output) throws IOException {
		// 序列化:输出
		output.writeInt(this.empno);
		output.writeUTF(this.ename);
		output.writeUTF(this.job);
		output.writeInt(this.mgr);
		output.writeUTF(this.hiredate);
		output.writeInt(this.sal);
		output.writeInt(this.comm);
		output.writeInt(this.deptno);
	}	
	
	public int getEmpno() {
		return empno;
	}
	public void setEmpno(int empno) {
		this.empno = empno;
	}
	public String getEname() {
		return ename;
	}
	public void setEname(String ename) {
		this.ename = ename;
	}
	public String getJob() {
		return job;
	}
	public void setJob(String job) {
		this.job = job;
	}
	public int getMgr() {
		return mgr;
	}
	public void setMgr(int mgr) {
		this.mgr = mgr;
	}
	public String getHiredate() {
		return hiredate;
	}
	public void setHiredate(String hiredate) {
		this.hiredate = hiredate;
	}
	public int getSal() {
		return sal;
	}
	public void setSal(int sal) {
		this.sal = sal;
	}
	public int getComm() {
		return comm;
	}
	public void setComm(int comm) {
		this.comm = comm;
	}
	public int getDeptno() {
		return deptno;
	}
	public void setDeptno(int deptno) {
		this.deptno = deptno;
	}

	
	
}

1.2 EmployeeMapper

//                                         k1偏移量   	     v1文本          k2 员工号  	 v2员工对象
public class EmployeeMapper extends Mapper<LongWritable, Text, IntWritable, Employee> {

	@Override
	protected void map(LongWritable key1, Text value1, Context context)
			throws IOException, InterruptedException {
		//7654,MARTIN,SALESMAN,7698,1981/9/28,1250,1400,30
		String data = value1.toString();
		
		//分词
		String[] words = data.split(",");
		
		//创建员工对象
		Employee e = new Employee();
		
		e.setEmpno(Integer.parseInt(words[0])); //员工号
		e.setEname(words[1]); //员工号
		e.setJob(words[2]); //职位
		e.setMgr(Integer.parseInt(words[3]));//老板号
		e.setHiredate(words[4]);//入职日期
		e.setSal(Integer.parseInt(words[5]));//月薪
		e.setComm(Integer.parseInt(words[6])); //奖金
		e.setDeptno(Integer.parseInt(words[7]));
		
		//输出   			k2员工号                                             v2员工对象
		context.write(new IntWritable(e.getEmpno()), e);
	}
}



1.3 EmployeeMain

public class EmployeeMain {

	public static void main(String[] args) throws Exception {
		// 1、创建任务Job,并且指定任务的入口
		Job job = Job.getInstance(new Configuration());
		job.setJarByClass(EmployeeMain.class);
		
		// 2、指定任务的Map、和Map的输出类型<k2   v2>
		job.setMapperClass(EmployeeMapper.class);
		job.setMapOutputKeyClass(IntWritable.class); // k2 
		job.setMapOutputValueClass(Employee.class); //v2 
		
		// 3、指定任务的Reduce、和Reduce输出的类型<k4   v4>
		job.setOutputKeyClass(IntWritable.class);  // k4
		job.setOutputValueClass(Employee.class); // v4
		
		// 4、指定任务的输入路径、和输出路径
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		FileOutputFormat.setOutputPath(job, new Path(args[1]));
		
		// 5、执行任务
		job.waitForCompletion(true);

	}

}

2、排序

Hadoop是默认进行数字升序的排序,字母大写在前小写在后,按字典顺序进行排序。
Hadoop是按照Map输出的key2进行排序
k2可以是

(1)基本的数据类型:数字:升序
                  字符串:字典顺序
 实现自定义的排序:继承  ****.Comparator
				 
(2)对象
	(*)一个列的排序:按照员工的薪水排序
	(*)多个列的排序:先按照部门号排序,再按照薪水
			select * 
			from emp
			order by deptno,sal;
				
接口:WritableComparable
	

2.1 实现int类型的自排序

//实现Int类型的自定义排序
public class MyIntWritableComparator extends IntWritable.Comparator {

	@Override
	public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
		// TODO Auto-generated method stub
		return -super.compare(b1, s1, l1, b2, s2, l2);
	}

}

然后再main函数里添加

//加入自定义的排序规则
job.setSortComparatorClass(MyIntWritableComparator.class);

2.2 实现text类型的自排序

同上

2.3 实现对象的自排序

只展示核心代码

public class Employee implements WritableComparable<Employee>{
	private int empno;
	private String ename;
	private String job;
	private int mgr;
	private String hiredate;
	private int sal;
	private int comm;
	private int deptno;
	
//	@Override
//	public int compareTo(Employee e) {
//		// 一个列的实现排序规则:按照员工的薪水排序
//		if(this.sal >= e.getSal()) {
//			return 1;
//		}else {
//			return -1;
//		}
//	}
	
	@Override
	public int compareTo(Employee e) {
		//实现多个列的排序:先按照部门号排序,再按照薪水
		if(this.deptno > e.getDeptno()) {
			return 1;
		}else if(this.deptno < e.getDeptno()) {
			return -1;
		}
		//等号放到最后
		if(this.sal >= e.getSal()) {
			return 1;
		}else {
			return -1;
		}
	}
	
}

3、分区Partition

MapReduce默认只有一个分区(输出文件),其根据Map的输出来建立分区。

3.1 什么是分区?

在这里插入图片描述
在这里拿sql执行计划进行举例,当sql进行查询的操作的时候,例如查询1500至2000之间的员工工资,是不是对整张表进行查询,这样的效率是不是比较低。
我们可以进行优化:
将整张表进行分区,分成三块,如上图,我们还是查询1500至2000之间的员工工资,这样我们只需要在分区1进行查询就可以,这样是不是提高了查询的效率
大家可以重点百度一下hash分区的思想,在这里就不多说了。

3.2 Java代码实现MapReduce分区

举例实现:将不同的部门好,分别存储在不同的文件里
1、创建MyEmployeePartitioner 类

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

//实现分区规则
//根据Map的输出<k2  v2>来建立分区                                                                    k2 部门号             v2  员工对象
public class MyEmployeePartitioner extends Partitioner<IntWritable, Employee> {

	@Override
	public int getPartition(IntWritable k2, Employee v2, int numTask) {
		// numTask:分区的个数
		
		//得到部门号
		int deptno = v2.getDeptno();
		
		if(deptno == 10) {
			//放入1号分区
			return 1%numTask;
		}else if(deptno == 20) {
			//放入2号分区
			return 2%numTask;
		}else {
			//放入0号分区
			return 3%numTask;
		}
	}

}

2、添加到main函数里,进行启动加载

//加入分区规则,几个分区
job.setPartitionerClass(MyEmployeePartitioner.class);
job.setNumReduceTasks(3); //分区的个数

4、combiner合并

4.1 什么是combiner

在这里插入图片描述

总之,combiner是一种特殊的Reducer,在Map端,先执行一次合并;用于减少输出到Reducer的数据量,减少网络的开销,提高性能,是MapReduce的一种优化
注意: 并不是所有的情况都能使用Combiner(eg:求平均)
wrong value class: class org.apache.hadoop.io.DoubleWritable
is not class org.apache.hadoop.io.IntWritable

引入Combiner以后,不能改变原理的逻辑,特别输出的数据类型搞清楚

4.2 Java代码实现

只需要在main函数启动类中,添加

//加入一个Combiner的class
job.setCombinerClass(reduce类.class);

三、MapReduce核心:Shuffle

在这里插入图片描述
shuffle也就是MapReduce的资源调度的过程,首先数据块读入程序之前,先转化为数据切片,然后,map再一条一条数据处理,数据处理之后,会放到一个环形缓冲区里,环形缓冲区达到80%之后,会触发一个检查点,数据发生溢写,剩余的20%将存放新的数据。发生溢写时,会进行两个操作,一个分区,一个排序。之后的数据才送到reduce,reduce的数据程序执行之前也会发生像map的shuffle操作,先从每个map拉取数据,由于是不同的map数据,因此先进行merge操作,再进行排序。

  • 为什么要进行排序?
    Reduce端需要对数据进行分组,将key相同的放在一起规约。为达到目的,有两种算法:hashmap和sort,前者太耗内存,而排序可以通过外排对任意数据量分组,只要磁盘数据量够大就行。map端排序是为了减轻reduce端排序的压力。

本质就是,map前后都有一个缓冲区,reduce前后也都有一个缓冲区。在缓冲区里有执行一些其他的操作。

四、MapReduce编程案例

1、实现distinct

去重:利用 k2和k3的关系,k2到k3的时候,有一个去重和分组的功能!

1.1 编写distinctMapper

public class distinctMapper  extends Mapper<LongWritable, Text,Text, NullWritable> {

    @Override
    protected void map(LongWritable key1, Text value1, Context context) throws IOException, InterruptedException {

        //取出数据
        String data = value1.toString();
        //分词
        String[] world = data.split(",");
        //输出
        context.write(new Text(world[2]),NullWritable.get());

    }
}

1.2 编写distinctReduce

public class distinctReduce extends Reducer<Text , NullWritable,Text,NullWritable> {

    @Override
    protected void reduce(Text key3, Iterable<NullWritable> values3, Context context) throws IOException, InterruptedException {

        //直接输出数据
        context.write(key3,NullWritable.get());
    }
}

1.3 distinctMain

拷贝上面主函数代码

2、多表查询

2.1map join:适用于有一张小表。输出部门名称、员工姓名

什么是mapjoin?
在这里插入图片描述
当一个表的数据较少时,可以在初始化的时候预先存入内存中,缓存小表,这样可以提高效率。注意mapjoin是每个map阶段都要加载setup方法进行缓存数据。
下面的代码举例:emp.csv、dept.csv表,输出部门名称、员工姓名进行举例
代码如下:
注意setup的代码。
(1)编写mapjoinMapper 类

public class mapjoinMapper extends Mapper<LongWritable, Text,Text,Text> {

    Map deptData = new HashMap();

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //7934,MILLER,CLERK,7782,1982/1/23,1300,0,10
        //拿数据进行分词
        String data = value.toString();
        String[] fields = data.split(",");

        String deptno = (String) deptData.get(fields[7]);
        //输出数据
        context.write(new Text(deptno) , new Text(fields[1]));
        //
        //
    }

    @Override
    protected void setup(Context context) throws IOException, InterruptedException {

        //1、拿到路径
        String path = context.getCacheFiles()[0].getPath();
        //2、拿到缓存文件名
        int index = path.lastIndexOf("/");
        String deptName = path.substring(index + 1);
        
        //3、按行读数据,并存入map集合中
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(deptName)));
        String s;
        while ((s=bufferedReader.readLine()) != null ){
            String[] fields = s.split(",");
            //40,OPERATIONS,BOSTON
            deptData.put(fields[0],fields[1]);

        }

    }
}

(2)编写mapjoinMain

import java.net.URI;
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;
public class mapjoinMain  {
    public static void main(String[] args) throws Exception {
        // 创建一个任务
        Job job = Job.getInstance(new Configuration());
        job.setJarByClass(mapjoinMain.class);

        job.setMapperClass(mapjoinMapper.class);
        job.setMapOutputKeyClass(Text.class); //k2 部门名称
        job.setMapOutputValueClass(Text.class); //v2 员工姓名

        //启动任务的时候,就将部门表进行缓存
        job.addCacheFile(new URI(args[0])); //第一参数代表部门表: /scott/dept.csv

        FileInputFormat.setInputPaths(job, new Path(args[1]));  //第二张表:员工表
        FileOutputFormat.setOutputPath(job, new Path(args[2])); //输出的路径

        // 5、执行任务
        job.waitForCompletion(true);
    }
}

2.2reduce join:适用于都是大表的情况

在这里插入图片描述
上图为reduce join的数据处理过程。

sql语句的写法:

查询员工信息:部门名称、员工名字
select d.dname,e.ename
from emp e,dept d
where e.deptno=d.deptno;

Java代码实现:
(1)ReduceJoinMapper 类

public class ReduceJoinMapper extends Mapper<LongWritable, Text, IntWritable,Text> {

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

        //1、拿数据进行分词
        String data = value.toString();
        String[] fields = data.split(",");
        //2、判断是emp还是dept文件
        if (fields.length == 3){
            //3、假如是dept文件,输出部门号,*部门名称
            context.write(new IntWritable(Integer.parseInt(fields[0])),new Text("*"+fields[1]));

        }else {
            //4、假如emp文件,输出部门号,员工名称
            context.write(new IntWritable(Integer.parseInt(fields[7])),new Text(fields[1]));
        }


    }
}

(2)ReduceJoinReducer 类

public class ReduceJoinReducer extends Reducer<IntWritable, Text,Text,Text> {

    @Override
    protected void reduce(IntWritable key3, Iterable<Text> values3, Context context) throws IOException, InterruptedException {

        String dept = "";  // 不能string dept = null 会报空指针异常!
        String empList = "";
        String temp;
        //1、遍历values3集合
        for (Text v : values3) {
            //2、判断出是部门号,还是姓名

            temp = v.toString();

            if (temp.startsWith("*")){
                dept = temp.substring(1);
            }else {
                empList = empList + "," + temp;
            }
        }
        //3、部门号作为k4进行输出,姓名进行累加,以字符串形式作为v4尽心输出
        context.write(new Text(dept) , new Text(empList) );

    }
}

(3)ReduceJoinMain 类

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 ReduceJoinMain {
    public static void main(String[] args) throws Exception {
        // 1、创建任务Job,并且指定任务的入口
        Job job = Job.getInstance(new Configuration());
        job.setJarByClass(ReduceJoinMain.class);

        // 2、指定任务的Map、和Map的输出类型<k2   v2>
        job.setMapperClass(ReduceJoinMapper.class);
        job.setMapOutputKeyClass(IntWritable.class); // k2 部门号
        job.setMapOutputValueClass(Text.class); //v2

        // 3、指定任务的Reduce、和Reduce输出的类型<k4   v4>
        job.setReducerClass(ReduceJoinReducer.class);
        job.setOutputKeyClass(Text.class);  // k4
        job.setOutputValueClass(Text.class); // v4

        // 4、指定任务的输入路径、和输出路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        // 5、执行任务
        job.waitForCompletion(true);
    }
}

3、链式处理:多个map、多个reduce

举例:编写MapReduce程序实现下面的需求。
需求一:根据员工的职位涨工资。总裁:1000;经理:800;其他人员:400
方式一:case   ...  when ....  (标准的SQL)
				
select empno,ename,job,sal 涨前,
       case job when 'PRESIDENT' then sal+1000
	            when 'MANAGER' then sal + 800
				else sal+ 400
	   end 涨后
from emp;
				
方式二:decode函数(是Oracle的函数)
				
select empno,ename,job,sal 涨前,
      decode(job,'PRESIDENT',sal+1000,
              'MANAGER',sal+800,
	            sal+400) 涨后
from emp;
		
		
需求二:根据部门号涨工资。10号部门:1000;20号部门:800;其他部门:400
需求三:求部门的工资总额。
需求四:求部门工资总额大于15000的部门。	

java代码实现如下:
(1)、创建Employee类,get与set方法自己生成

public class Employee implements Writable {

    private int empno;
    private String ename;
    private String job;
    private int mgr;
    private String hiredate;
    private int sal;
    private int comm;
    private int deptno;

    @Override
    public void readFields(DataInput input) throws IOException {
        // 反序列化:输入
        this.empno = input.readInt();
        this.ename = input.readUTF();
        this.job = input.readUTF();
        this.mgr = input.readInt();
        this.hiredate = input.readUTF();
        this.sal = input.readInt();
        this.comm = input.readInt();
        this.deptno = input.readInt();
    }
    @Override
    public void write(DataOutput Output) throws IOException {
//      序列化输出
        Output.writeInt(this.empno);
        Output.writeUTF(this.ename);
        Output.writeUTF(this.job);
        Output.writeInt(this.mgr);
        Output.writeUTF(this.hiredate);
        Output.writeInt(this.sal);
        Output.writeInt(this.comm);
        Output.writeInt(this.deptno);
    }
    
}

(2)、创建GetEmployeeMap,这个mapper的功能主要是把取得每条数据,封装在employee对象里面

public class GetEmployeeMap extends Mapper<LongWritable, Text, IntWritable,Employee> {

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        /*
        * 1、读到一条数据
        * 2、进行分词,得到一个数组
        * 3、对整个employee 进行封装数据
        * 4、进行输出
        *
        * */
        String data = value.toString();

        String[] words = data.split(",");

        Employee e = new Employee();
        e.setEmpno(Integer.parseInt(words[0])); //员工号
        e.setEname(words[1]); //员工号
        e.setJob(words[2]); //职位
        e.setMgr(Integer.parseInt(words[3]));//老板号
        e.setHiredate(words[4]);//入职日期
        e.setSal(Integer.parseInt(words[5]));//月薪
        e.setComm(Integer.parseInt(words[6])); //奖金
        e.setDeptno(Integer.parseInt(words[7]));

        context.write(new IntWritable(e.getEmpno()),e);

    }
}

(3)、创建IncreaseSalaryByJobMap,主要实现根据员工职位涨工资。

public class IncreaseSalaryByJobMap extends Mapper<IntWritable,Employee,IntWritable,Employee> {

    @Override
    protected void map(IntWritable key, Employee emp, Context context) throws IOException, InterruptedException {

        /*
        * 1、读取员工职位
        * 2、判断是什么职位
        * 3、进行输出
        *
        * */
        String job = emp.getJob();

        if(job.equals("PRESIDENT")) {
            emp.setSal(emp.getSal() + 1000); //总裁涨1000
        }else if(job.equals("MANAGER")) {
            emp.setSal(emp.getSal() + 800); //经理涨800
        }else {
            emp.setSal(emp.getSal() + 400);
        }

        context.write(new IntWritable(emp.getEmpno()),emp);


    }
}

(4)、创建IncreaseSalaryByDeptMap , 主要实现根据部门号涨工资

public class IncreaseSalaryByDeptMap extends Mapper<IntWritable,Employee,IntWritable,Employee> {
    @Override
    protected void map(IntWritable empno, Employee emp, Context context) throws IOException, InterruptedException {
        /*
        *
        * 1、取出部门号
        * 2、进行判断
        * 3、输出
        * */
        int deptno = emp.getDeptno();

        if(deptno == 10) {
            emp.setSal(emp.getSal() + 1000);
        }else if(deptno == 20) {
            emp.setSal(emp.getSal() + 800);
        }else {
            emp.setSal(emp.getSal() + 400);
        }

        context.write(new IntWritable(deptno),emp);
    }
}

(5)、创建GetTotalSalaryBydeptnoReduce,主要实现部门的工资总额。在这块是reduce操作,也就是一个分组操作。

public class GetTotalSalaryBydeptnoReduce extends Reducer<IntWritable,Employee,IntWritable,IntWritable> {

    @Override
    protected void reduce(IntWritable deptno, Iterable<Employee> emp, Context context) throws IOException, InterruptedException {

        int totalSalary =0;

        for (Employee employee : emp) {
           totalSalary = totalSalary + employee.getSal();
        }

        context.write(deptno , new IntWritable(totalSalary));


    }
}

(6)、创建FilterTotalSalaryByDeptnoMap,主要筛选出工资大于15000的部门

public class FilterTotalSalaryByDeptnoMap extends Mapper<IntWritable, IntWritable, IntWritable, IntWritable> {

    @Override
    protected void map(IntWritable deptno, IntWritable total, Context context) throws IOException, InterruptedException {


        if (total.get() > 15000){

            context.write(deptno, total);

        }

    }
}

(7)、创建MyChainMain,注意导入的类,chainmapper与chainReduce导入的lib下的chain

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

/**
 * @author 吴小辉
 * @date 2020/9/16-${TIEM}
 */
public class MyChainMain  {
    public static void main(String[] args) throws  Exception{

        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);
        job.setJarByClass(MyChainMain.class);

        //指定链式处理的每个Map和Reduce
        ChainMapper.addMapper(job, GetEmployeeMap.class,
                LongWritable.class, Text.class, IntWritable.class, Employee.class, conf);

        ChainMapper.addMapper(job, IncreaseSalaryByJobMap.class,
                IntWritable.class, Employee.class, IntWritable.class, Employee.class, conf);

        ChainMapper.addMapper(job, IncreaseSalaryByDeptMap.class,
                IntWritable.class, Employee.class, IntWritable.class, Employee.class, conf);

        ChainReducer.setReducer(job, GetTotalSalaryBydeptnoReduce.class,
                IntWritable.class, Employee.class, IntWritable.class, IntWritable.class, conf);

        ChainReducer.addMapper(job, FilterTotalSalaryByDeptnoMap.class,
                IntWritable.class, IntWritable.class, IntWritable.class, IntWritable.class, conf);

        FileInputFormat.setInputPaths(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        job.waitForCompletion(true);

    }
}

上面的代码提供的只是一个思想,如果读者没有数据的话,可以用自己的数据,只需要把Employee类进行根据自己的数据进行修改一下就行。

4、倒排索引

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值