MapReduce使用与原理(一)

MapReduce 快速入门

MapperReduce代码编写组成

一个MapReduce程序由三部分组成:Mapper、Reducer、Dirver。

Mapper

编写Mapper部分需要用户自定义一个类,并继承Hadoop 中的Mapper类,Mapper以行为单位读取split中的数据,转换成K,V格式,K表示读取当前行数据的offset偏移量,V表示读取当前行数据的内容。针对读取过来的数据用户需要在自定义Mapper类中实现map方法完成相应业务逻辑处理数据,读取到的每行数据都会调用一次map方法,最终返回一条K,V对的数据,便于Reduce端拉取。

Reducer

编写Reducer部分也需要用户自定义一个类,并继承Hadoop的Reducer类,Reducer端输入的数据类型是Mapper的输出的K,V类型。从Map端拉取过来的数据会按照key组进行处理,每组数据<K,V>调用一次reduce()方法。

Driver

编写好Mapper和Reducer代码后需要将Mapper和Reducer最终写入到一个Java类中,这个类就会被称作为Driver类,Driver类中包括MapReduce作业的整体逻辑,包括配置作业、设置Mapper和Reducer类、指定输入

代码案例

  1. 编写Reducer代码

创建WordCountReducer 代码如下:

java 复制代码
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

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

    //创建写出的value
    IntWritable total = new IntWritable();

    //每组key会调用一次
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
        int sum = 0;

        //累加
        for (IntWritable value : values) {
            sum += value.get();
        }

        //设置当前key对应value结果值
        total.set(sum);

        //结果写出
        context.write(key,total);
    }
}

以上Reducer&#x3c;Text, IntWritable,Text,IntWritable>中泛型表示从Map端获取K,V格式数据key的类型、从Map端获取K,V格式化数据Value的类型、Reduce处理完数据写出数据Key的类型、Reduce处理完数据写出数据Value的类型。

  1. 编写Driver代码

Driver中主要将Mapper和Redcuer代码设置在一起,并设置对应的参数。有如下几个步骤需要设置:

(1) 获取配置信息及job对象。

(2) 设置Driver 程序对应的jar/类。

(3) 设置Mapper和Reducer对应的类。

(4) 设置Mapper输出key、value类型。

(5) 设置最终输出K,V类型。

(6) 设置数据输入和结果写出路径。

(7) 运行任务。

java 复制代码
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 WordCountDriver {
    public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
        if(args.length!=2){
            System.out.println("输入输入路径和输出路径参数");
            System.exit(1);
        }
        //1.获取配置信息及job对象
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);

        //2.设置Driver 程序对应的jar/类
        job.setJarByClass(WordCountDriver.class);

        //3.设置Mapper和Reducer对应的类
        job.setMapperClass(WordCountMapper.class);
        job.setReducerClass(WordCountReducer.class);

        //4.设置Mapper输出key、value类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);

        //5.设置最终输出K,V类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        //6.设置数据输入和结果写出路径
        FileInputFormat.setInputPaths(job,new Path(args[0]));
        FileOutputFormat.setOutputPath(job,new Path(args[1]));

        //7.运行任务,运行成功返回true
        //参数true 表示打印job执行的信息,例如:读取数据大小,读取和写出条数等。
        boolean success = job.waitForCompletion(true);
        if (success) {
            // 任务执行成功的逻辑
            System.out.println("任务执行成功");
        } else {
            // 任务执行失败的逻辑
            System.out.println("任务执行失败");
        }

    }
}

以上代码读取数据是本地文件数据,我们也可以将数据文件上传到HDFS中读取分析,然后将结果写出到HDFS路径中。

MapReduce数据序列化

MapReduce中Map阶段和Reduce阶段需要将数据写入到磁盘,这就涉及到数据序列化与反序列化,Hadoop中提供了自定义的序列化类型Writable类型,这些类型是基于Hadoop的Writable接口实现的,Writable类型在Hadoop中通常用于表示MapReduce作业的输入和输出键值对的键和值类型,可以结合Java中的序列化类型来理解Writable类型,相比于Java 序列化类型,Writable类型可以避免一些Java序列化中的开销,提供的序列化方式更高效。

自定义序列化

自定义序列化案例

  1. 自定义Bean
java 复制代码
/**
 * 自定义类 实现 writable 接口
 */
public class CarInfo implements Writable {

    //1.定义空构造
    public CarInfo() {
    }

    //2.定义属性
    private String car ;
    private Double avgSpeed ;
    private Double totalKm ;

    //3.设置get set方法和toString方法
    public String getCar() {
        return car;
    }

    public void setCar(String car) {
        this.car = car;
    }

    public Double getAvgSpeed() {
        return avgSpeed;
    }

    @Override
    public String toString() {
        return car + "\t"+ avgSpeed +"\t"+ totalKm ;
    }

    public void setAvgSpeed(Double avgSpeed) {
        this.avgSpeed = avgSpeed;
    }

    public Double getTotalKm() {
        return totalKm;
    }

    public void setTotalKm(Double totalKm) {
        this.totalKm = totalKm;
    }

    //3.实现序列化、反序列化方法
    @Override
    public void write(DataOutput out) throws IOException {
        out.writeUTF(car);
        out.writeDouble(avgSpeed);
        out.writeDouble(totalKm);
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        this.car = in.readUTF();
        this.avgSpeed = in.readDouble();
        this.totalKm = in.readDouble();
    }

}

  1. Driver代码
java 复制代码
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 CarAnalyDriver {
    public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
  
        //1.获取配置信息及job对象
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);

        //2.设置Driver 程序对应的jar/类
        job.setJarByClass(CarAnalyDriver.class);

        //3.设置Mapper和Reducer对应的类
        job.setMapperClass(CarAnalyMapper.class);
        job.setReducerClass(CarAnalyReducer.class);

        //4.设置Mapper输出key、value类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(CarInfo.class);

        //5.设置最终输出K,V类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(CarInfo.class);

        //6.设置数据输入和结果写出路径
        FileInputFormat.setInputPaths(job,new Path("data/carinfo.txt"));
        FileOutputFormat.setOutputPath(job,new Path("output/"));

        //7.运行任务,运行成功返回true
        //参数true 表示打印job执行的信息,例如:读取数据大小,读取和写出条数等
        boolean success = job.waitForCompletion(true);
        if (success) {
            // 任务执行成功的逻辑
            System.out.println("任务执行成功");
        } else {
            // 任务执行失败的逻辑
            System.out.println("任务执行失败");
        }
    }
}

MapReduce Shuffle

在MapReduce中有一个非常重要的概念:Shuffle。根据之前学习过的MapReduce数据处理流程,我们知道,在Map阶段处理完数据后,会将具有相同key的数据进行重新分区、排序,并最终将每个Map任务的输出合并成一个文件。然后,在Reduce阶段通过数据拷贝将数据传送到相应的Reduce任务进行处理。这个过程就是Shuffle过程的核心。简而言之,MapReduce中的Shuffle过程指的是在Map方法执行后、Reduce方法执行前对数据进行处理和准备的阶段。

上图Shuffle更加详细的流程如下图所示,Map Task处理完的数据首先写入到默认100M的环形缓冲区,底层使用数组是实现,一半存储数据,一般存储数据对应的索引信息,当环形缓冲区中的空间被使用到80%时数据会发生溢写,同时,会找到剩余空间的中间位置反向继续写入数据和索引,这样做的好处是更好的利用存储空间存储更多的数据。溢写的数据会经过分区、快速排序形成小文件数据,用户可以选择是否使用Combiner进行map预聚合,如果使用每个分区内的数据还会进行合并,多次溢写的小文件最终会通过归并排序合并成一个大磁盘文件,如果设置了压缩,会将数据压缩后最终写入磁盘文件。

Reduce Task会将多个Map Task处理的数据复制到Redcue端,首先会放入内存缓冲区中,当内存不足时会将数据写入到磁盘文件,后续经过归并排序将从不同的Map Task拉取过来的数据合并成一个文件,根据相同的key分成对应的一组组数据,最终被Reduce Task处理。

Shuffle阶段包括如下几个步骤:

分区(Partitioning):根据键值对的键,将中间键值对划分到不同的分区。每个分区对应一个Reduce任务,这样可以确保相同键的键值对被发送到同一个Reduce任务上进行处理。

排序(Sorting):对每个分区内的中间键值对按键进行排序(快排)。通过排序,相同键的键值对会相邻存放,以便后续的合并操作更高效。

合并(Merging):对多次溢写的结果按照分区进行归并排序合并溢写文件,每个maptask最终形成一个磁盘一些文件,减少后续Reduce阶段的输入数据量。

Combiner(局部合并器):Combiner是一个可选的优化步骤,在Map任务输出结果后、Reduce输入前执行。其作用是对Map任务的输出进行局部合并,将具有相同键的键值对合并为一个,以减少需要传输到Reduce节点的数据量,降低网络开销,并提高整体性能。Combiner实际上是一种轻量级的Reduce操作,用于减少数据在网络传输过程中的负担。需要注意的是,Combiner的执行并不是强制的,而是由开发人员根据具体情况决定是否使用。

拷贝(Copying):将各分区内的数据复制到各自对应的Reduce任务节点上,会先向内存缓冲区中存放数据,内存不够再溢写磁盘,当所有数据复制完毕后,Reduce Task统一对内存和磁盘数据进行归并排序并交由Redcue方法并行处理。

以上过程共同构成了Shuffle过程,在MapReduce中起着重要的作用,用于重新组织和准备数据,并在Reduce阶段进行最终的计算和处理。

相关推荐
Volunteer Technology1 小时前
MapReduce使用与原理 (二)
大数据·mapreduce
石逸凡1 小时前
新时代的信息茧房
大数据·人工智能
澈2071 小时前
Git入门指南:核心概念与实用操作
大数据·git·搜索引擎
workflower2 小时前
人工智能全球治理
大数据·人工智能·设计模式·机器人·动态规划
workflower2 小时前
AI灵活高效的智慧用能核心场景
大数据·人工智能·设计模式·机器人·动态规划
高粱2 小时前
Dbeaver 搜索自动加引号
eclipse
189228048612 小时前
NV301固态MT29F32T08GWLBHD6-QJES:B
大数据·服务器·人工智能·科技·缓存
海兰2 小时前
在 Grafana 中驾驭 ES|QL:Elasticsearch 管道查询实战指南
大数据·elasticsearch·grafana
zuozewei2 小时前
AI-7D-SATS平台的harness engineering设计:让 AI Agent 从“工具堆叠”长成“工程制品”
大数据·人工智能