Hadoop Map/Reduce

Posted on 2024-01-03 16:08  打杂滴  阅读(9)  评论(0编辑  收藏  举报

 

MapReduce是Google提出的一个软件架构,用于大规模数据集(大于1TB)的并行运算。概念“Map(映射)”和“Reduce(归纳)”

映射和归纳

    1. 映射
      一个映射函数就是对一些独立元素组成的概念上的列表(例如,一个测试成绩的列表)的每一个元素进行指定的操作(比如,有人发现所有学生的成绩都被高估了一分,他可以定义一个“减一”的映射函数,用来修正这个错误。)。事实上,每个元素都是被独立操作的,而原始列表没有被更改,因为这里创建了一个新的列表来保存新的答案。这就是说,Map操作是可以高度并行的,这对高性能要求的应用以及并行计算领域的需求非常有用。
    2. 归纳
      归纳操作指的是对一个列表的元素进行适当的合并(继续看前面的例子,如果有人想知道班级的平均分该怎么做?他可以定义一个归纳函数,通过让列表中的奇数(odd)或偶数(even)元素跟自己的相邻的元素相加的方式把列表减半,如此递归运算直到列表只剩下一个元素,然后用这个元素除以人数,就得到了平均分)。虽然他不如映射函数那么并行,但是因为归纳总是有一个简单的答案,大规模的运算相对独立,所以归纳函数在高度并行环境下也很有用。

Map/Reduce框架运转在<key, value> 键值对上,也就是说, 框架把作业的输入看为是一组<key, value> 键值对,同样也产出一组 <key, value> 键值对做为作业的输出,这两组键值对的类型可能不同。

框架需要对key和value的类(classes)进行序列化操作, 因此,这些类需要实现 Writable接口。 另外,为了方便框架执行排序操作,key类必须实现 WritableComparable接口。

一个Map/Reduce 作业的输入和输出类型如下所示:

(input) <k1, v1> -> map -> <k2, v2> -> combine -> <k2, v2> -> reduce -> <k3, v3> (output)

 

应用程序通常会通过提供map和reduce来实现 Mapper和Reducer接口,它们组成作业的核心。

Mapper

public static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable>

1. Mapper组件开发方式:自定义一个类,继承Mapper

2. Mapper组件的作用是定义每一个MapTask具体要怎么处理数据。例如一个文件,256MB,会生成2个MapTask(每个切片大小,默认是128MB,所以MapTask的多少有处理的数据大小来决定)。即2个MapTask处理逻辑是一样的,只是每个MapTask处理的数据不一样。

3.下面是Mapper类中的4个泛型含义:
a.泛型一:KEYIN: Longwritable,对应的Mapper的输入key。输入key是每行的行首偏移里
b.泛型二: VALUEIN: Text,对应的Mapper的输入Value。输入value是每行的内容
c.泛型三:KEYOUT: 对应的Mapper的输出key,根据业务来定义
d.泛型四:VALUEOUT:对应的Mapper的输出value,根据业务来定义
4.注意:初学时,KEYIN和VALUEIN写死(LongWritable,Text)。KEYOUT和VALUEOUT不固定,根据业务来定
5.Writable机制是Hadoop自身的序列化机制,常用的类型:
a. LongWritable
b. Text(String)
c. IntWritable
d. NullWritable
6.定义MapTask的任务逻辑是通过重写map()方法来实现的。
读取一行数据就会调用一次此方法,同时会把输入key和输入value进行传递
7.在实际开发中,最重要的是拿到输入value(每行内容)
8. 输出方法:通过context.write(输出key,输出value)
9.开发一个MapReduce程序(job),Mapper可以单独存储,
此时,最后的输出的结果文件内容就是Mapper的输出。
10. Reducer组件不能单独存在,因为Reducer要依赖于Mapper的输出。
当引入了Reducer之后,最后输出的结果文件的结果就是Reducer的输出。

Reducer

public static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable>

 

1. Reducer组件用于接收Mapper组件的输出
2.reduce的输入key,value需要和mapper的输出key,value类型保持一致
3.reduce的输出key,value类型,根据具体业务决定
4.reduce收到map的输出,会按相同的key做聚合,
形成:key Iterable 形式然后通过reduce方法进行传递
5.reduce方法中的Iterable是一次性的,即遍历一次之后,再遍历,里面就没有数据了。
所以,在某些业务场景,会涉及到多次操作此迭代器,处理的方法是
:@先创建一个List @把Iterable装到List @多次去使用List即可

 

 

----------------

Hadoop中的Mapper是MapReduce编程模型中的一个组件,用于将输入数据划分为一系列键值对,并对每个键值对进行处理。Mapper负责处理输入数据的切分、映射和中间结果的输出。

Mapper类在Hadoop中是一个抽象类,需要用户自定义一个继承自Mapper类的子类,并实现其map方法。map方法的签名如下:

protected void map(KEYIN key, VALUEIN value, Context context)
    throws IOException, InterruptedException

其中,KEYIN表示输入键的类型,VALUEIN表示输入值的类型,Context是Mapper的上下文对象,用于与Hadoop框架进行交互。

在map方法中,用户可以根据输入键值对的内容进行处理,并使用Context对象将中间结果输出。通常情况下,Mapper的输出键值对类型与输入键值对类型不同,可以通过泛型参数指定输出键值对的类型。例如,可以将输入的文本行切分为单词,并将每个单词作为键,将计数1作为值进行输出。

以下是一个简单的Mapper示例,它将输入的文本行切分为单词,并输出每个单词的计数为1:

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

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

    public void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {
        String line = value.toString();
        String[] words = line.split(" ");

        for (String w : words) {
            word.set(w);
            context.write(word, one);
        }
    }
}

在上述示例中,我们自定义了一个继承自Mapper类的子类MyMapper,并重写了map方法。在map方法中,我们首先将输入的Text类型的value转换为字符串,然后使用空格进行分词,将每个单词作为键,将计数1作为值进行输出。

需要注意的是,Mapper的输入可以是任意类型的键值对,用户需要根据实际情况指定输入键值对的类型,并根据输入数据的格式和处理逻辑编写map方法的实现。Mapper的输出会作为中间结果传递给Reducer进行进一步处理。

 

Hadoop中的Reducer是MapReduce编程模型中的一个组件,用于对Map阶段的输出进行合并和聚合操作,生成最终的输出结果。Reducer接受来自Mapper阶段的键值对数据集,并按照键进行分组,然后对每个键的值集合进行处理。

Reducer类在Hadoop中是一个抽象类,需要用户自定义一个继承自Reducer类的子类,并实现其reduce方法。reduce方法的签名如下:

protected void reduce(KEYIN key, Iterable<VALUEIN> values, Context context)
    throws IOException, InterruptedException

其中,KEYIN表示输入键的类型,VALUEIN表示输入值的类型,Context是Reducer的上下文对象,用于与Hadoop框架进行交互。

在reduce方法中,用户可以通过迭代器Iterable<VALUEIN> values来遍历当前键的所有值,并对这些值进行处理。Reducer的输出可以使用Context对象的write方法写入最终的输出。

以下是一个简单的Reducer示例,它将输入的键值对进行合并,并计算每个键的总和:

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

public class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
    private IntWritable result = new IntWritable();

    public void reduce(Text key, Iterable<IntWritable> values, Context context)
            throws IOException, InterruptedException {
        int sum = 0;
        for (IntWritable value : values) {
            sum += value.get();
        }
        result.set(sum);
        context.write(key, result);
    }
}

在上述示例中,我们自定义了一个继承自Reducer类的子类MyReducer,并重写了reduce方法。在reduce方法中,我们遍历了每个键的所有值,并将它们累加到变量sum中,然后将结果写入输出。

需要注意的是,Reducer的输入键值对是按照键进行分组的,因此在reduce方法中,可以假设所有具有相同键的值都会被传递给同一个reduce方法进行处理。用户需要根据实际需求编写reduce方法的逻辑,对值进行合并、计算或其他操作,生成最终的输出结果。

 

重写Mapper方法

protected void map(LongWritable key, Text value,Mapper<LongWritable, Text, Text,FlowBean>.Context context)throws IOException,InterruptedException {

}

//Mapper<LongWritable,Text, Text,FlowBean>.Context context 表示上下文工具,用于将数据返回worker

 

Hadoop中Context类的作用和Mapper<LongWritable, Text, Text, LongWritable>.Context context

Mapper<LongWritable, Text, Text, LongWritable>.Context

在MapReduce中,Mapper类的context参数是用于与MapReduce框架进行交互的上下文对象。它提供了访问Mapper的输入和输出以及其他与作业执行相关的信息的方法。

在给定的代码示例中,Mapper的输入键类型为LongWritable,输入值类型为Text,输出键类型为Text,输出值类型为LongWritable。因此,context参数的类型应该是Mapper<LongWritable, Text, Text, LongWritable>.Context。

通过context对象,可以执行以下操作:

  1. 获取输入数据:可以使用context对象的方法获取当前Mapper正在处理的输入键值对。例如,可以使用context.getCurrentKey()获取当前输入键的值,使用context.getCurrentValue()获取当前输入值的值。

  2. 发出输出数据:可以使用context对象的方法将Mapper的输出键值对发送到Reducer或输出。例如,可以使用context.write(key, value)方法发送输出键值对。

  3. 记录日志:可以使用context对象的方法记录日志消息。例如,可以使用context.getCounter()获取计数器对象,并使用计数器对象的方法增加计数器的值。

  4. 获取配置信息:可以使用context对象的方法获取作业的配置信息。例如,可以使用context.getConfiguration()获取作业的配置对象,并使用配置对象的方法获取作业的参数。

总之,context参数提供了与MapReduce框架进行交互的方法和功能,使Mapper能够处理输入数据并生成输出数据。

 

在MapReduce中,Reducer类的reduce方法的签名通常如下所示:

reduce(Text k2, Iterable<LongWritable> v2s, Reducer<Text, LongWritable, Text, LongWritable>.Context context)

这个方法用于对Mapper的输出进行聚合和处理,并生成最终的输出结果。下面是对这个方法的参数进行解释:

  • Text k2:表示输入键的类型。在这个方法中,它表示Mapper的输出键类型。
  • Iterable<LongWritable> v2s:表示输入值的类型。在这个方法中,它表示Mapper的输出值类型的集合,因为一个键可能对应多个值。
  • Reducer<Text, LongWritable, Text, LongWritable>.Context context:表示与MapReduce框架进行交互的上下文对象。

通过这个方法,可以对相同键的值进行迭代,并进行聚合、计算或其他操作。在Reducer中,可以使用context对象来执行以下操作:

  1. 发出输出数据:可以使用context对象的方法将Reducer的输出键值对发送到最终的输出。例如,可以使用context.write(key, value)方法发送输出键值对。

  2. 记录日志:可以使用context对象的方法记录日志消息。例如,可以使用context.getCounter()获取计数器对象,并使用计数器对象的方法增加计数器的值。

  3. 获取配置信息:可以使用context对象的方法获取作业的配置信息。例如,可以使用context.getConfiguration()获取作业的配置对象,并使用配置对象的方法获取作业的参数。

总之,reduce方法通过迭代输入值的集合,对相同键的值进行聚合和处理,并将结果发送到最终的输出。context参数提供了与MapReduce框架进行交互的方法和功能,使Reducer能够生成最终的输出结果。

 

 

------------------

以下是一个完整的MapReduce WordCount代码示例,使用Java编写,并使用Hadoop作为MapReduce框架:

import java.io.IOException;
import java.util.StringTokenizer;

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.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class WordCount {

  public static class TokenizerMapper
       extends Mapper<Object, Text, Text, IntWritable>{

    private final static IntWritable one = new IntWritable(1);
    private Text word = new Text();

    public void map(Object key, Text value, Context context
                    ) throws IOException, InterruptedException {
      StringTokenizer itr = new StringTokenizer(value.toString());
      while (itr.hasMoreTokens()) {
        word.set(itr.nextToken());
        context.write(word, one);
      }
    }
  }

  public static class IntSumReducer
       extends Reducer<Text,IntWritable,Text,IntWritable> {
    private IntWritable result = new IntWritable();

    public void reduce(Text key, Iterable<IntWritable> values,
                       Context context
                       ) throws IOException, InterruptedException {
      int sum = 0;
      for (IntWritable val : values) {
        sum += val.get();
      }
      result.set(sum);
      context.write(key, result);
    }
  }

  public static void main(String[] args) throws Exception {
    Configuration conf = new Configuration();
    Job job = Job.getInstance(conf, "word count");
    job.setJarByClass(WordCount.class);
    job.setMapperClass(TokenizerMapper.class);
    job.setCombinerClass(IntSumReducer.class);
    job.setReducerClass(IntSumReducer.class);
    job.setOutputKeyClass(Text.class);
    job.setOutputValueClass(IntWritable.class);
    FileInputFormat.addInputPath(job, new Path(args[0]));
    FileOutputFormat.setOutputPath(job, new Path(args[1]));
    System.exit(job.waitForCompletion(true) ? 0 : 1);
  }
}

上述代码示例中的TokenizerMapper类是Mapper类,它继承自Hadoop的Mapper类,并重写了map方法。在map方法中,输入的文本被拆分为单词,并为每个单词发射一个键值对(单词, 1)。

IntSumReducer类是Reducer类,它继承自Hadoop的Reducer类,并重写了reduce方法。在reduce方法中,相同单词的计数被累加为总计数,并发射一个键值对(单词, 总计数)。

main方法中,首先创建一个Hadoop作业(Job)对象,并设置作业的相关配置。然后,指定Mapper类和Reducer类,以及输入路径和输出路径。最后,运行作业并等待完成。

请注意,上述代码示例是使用Java编写的,并使用Hadoop作为MapReduce框架。您需要在Hadoop集群上配置和运行此代码。具体的配置和运行步骤可能因环境而异

 

Copyright © 2024 打杂滴
Powered by .NET 8.0 on Kubernetes