大数据上课笔记之初识MapReduce

目录

一、课程初识

二、知识讲解

1、MapReduce核心思想

 2、MapReduce编程模型

三、MapReduce编程实例思路

1、Map阶段(映射阶段)

2、Reduce阶段(归并阶段) 

 四、MapReduce编程实例操作

1、准备数据文件

1.1、在虚拟机上创建文本文件

1.2、上传文件到HDFS指定目录

2、创建Maven项目

3、添加相关依赖

4、创建日志属性文件

5、创建词频统计映射器类

6、创建词频统计驱动器类

7、运行词频统计驱动器类,查看结果 

8、修改词频统计映射器类 

9、修改词频统计驱动器类

10、启动词频统计驱动器类,查看结果 

 11、创建词频统计归并器类

12、修改词频统计驱动器类 

 13、运行词频统计驱动器类,查看结果

14、修改词频统计归并器类

15、修改词频统计驱动器类 

 16、启动词频统计驱动器类,查看结果

17、采用多个Reduce做合并

17.1、MR默认采用哈希分区HashPartitioner

17.2、修改词频统计驱动器类,设置分区数量 

五、课后复习


一、课程初识

  • MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算。概念"Map(映射)"和"Reduce(归约)",是它们的主要思想,都是从函数式编程语言里借来的,还有从矢量编程语言里借来的特性。它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。 当前的软件实现是指定一个Map(映射)函数,用来把一组键值对映射成一组新的键值对,指定并发的Reduce(归约)函数,用来保证所有映射的键值对中的每一个共享相同的键组。

二、知识讲解

1、MapReduce核心思想

  • MapReduce的核心思想是“分而治之”。所谓“分而治之”就是把一个复杂的问题,按照一定的“分解”方法分为等价的规模较小的若干部分,然后逐个解决,分别找出各部分的结果,把各部分的结果组成整个问题的结果,这种思想来源于日常生活与工作时的经验,同样也完全适合技术领域。
  • MapReduce作为一种分布式计算模型,它主要用于解决海量数据的计算问题。使用MapReduce操作海量数据时,每个MapReduce程序被初始化为一个工作任务,每个工作任务可以分为Map和Reduce两个阶段。
阶段功能
Map阶段负责将任务分解,即把复杂的任务分解成若干个“简单的任务”来并行处理,但前提是这些任务没有必然的依赖关系,可以单独执行任务。
Reduce阶段负责将任务合并,即把Map阶段的结果进行全局汇总。
  • MapReduce就是“任务的分解与结果的汇总”。即使用户不懂分布式计算框架的内部运行机制,但是只要能用Map和Reduce思想描述清楚要处理的问题,就能轻松地在Hadoop集群上实现分布式计算功能。

  • MapReduce设计的一个理念就是“计算向数据靠拢”,而不是“数据向计算靠拢”,因为,移动数据需要大量的网络传输开销。

 2、MapReduce编程模型

  • MapReduce是一种编程模型,用于处理大规模数据集的并行运算。使用MapReduce执行计算任务的时候,每个任务的执行过程都会被分为两个阶段,分别是Map和Reduce,其中Map阶段用于对原始数据进行处理,Reduce阶段用于对Map阶段的结果进行汇总,得到最终结果。

  •  Map和Reduce函数

三、MapReduce编程实例思路

1、Map阶段(映射阶段)

  • 输入键值对⟹ \Longrightarrow⟹输出键值对

2、Reduce阶段(归并阶段) 

 四、MapReduce编程实例操作

  • 启动hadoop服务

1、准备数据文件

1.1、在虚拟机上创建文本文件

  • 创建wordcount目录,在里面创建words.txt文件

1.2、上传文件到HDFS指定目录

  • 创建/wordcount/input目录,执行命令:hdfs dfs -mkdir -p /wordcount/input

 

  • 将文本文件words.txt,上传到HDFS的/wordcount/input目录

  • 在Hadoop WebUI界面上查看上传的文件

2、创建Maven项目

  • 创建Maven项目 - MRWordCount 

3、添加相关依赖

  • pom.xml文件里添加hadoopjunit依赖

4、创建日志属性文件

  • resources目录里创建log4j.properties文件

log4j.rootLogger=INFO, stdout, logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/wordcount.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

5、创建词频统计映射器类

  • 创建net.rzh.mr包,在包里创建WordCountMapper

  • 为了更好理解Mapper类的作用,在map()函数里暂时不进行每行文本分词处理,直接利用context输出keyvalue
package net.hw.mr;

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

import java.io.IOException;

/**
 * 功能:词频统计映射器类
 * 作者:华卫
 * 日期:2022年12月07日
 */
public class WordCountMapper extends Mapper<LongWritable, Text, LongWritable, Text> {
    @Override
    protected void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {
        // 直接将键值对数据传到下一个阶段
        context.write(key, value);
    }
}

  •  Mapper<泛型参数1, 泛型参数2, 泛型参数3, 泛型参数4>参数说明
泛型参数说明
KEYIN输入类型(InputKeyClass)
VALUEIN输入类型(InputValueClass)
VALUEIN输出类型(OutputKeyClass)
VALUEOUT输出类型(OutputValueClass)
  • 注意:MR应用,必须有映射器(Mapper),但是归并器(Reducer)可有可无
  • Java数据类型与Hadoop数据类型对应关系
Java数据类型Hadoop数据类型
StringText
intIntWritable
longLongWritable
floatFloatWritable
doubleDoubleWritable
  • Hadoop类型数据调用get()方法就可以转换成Java类型数据
  • Java类型数据通过new XXXWritable(x)方式转换成对应的Hadoop类型数据

6、创建词频统计驱动器类

  • net.rzh.mr包里创建WordCountDriver

package net.rzh.mr;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileStatus;
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.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.net.URI;


public class WordCountDriver {
    public static void main(String[] args) throws Exception {
        // 创建配置对象
        Configuration conf = new Configuration();
        // 设置数据节点主机名属性
        conf.set("dfs.client.use.datanode.hostname", "true");

        // 获取作业实例
        Job job = Job.getInstance(conf);
        // 设置作业启动类
        job.setJarByClass(WordCountDriver.class);

        // 设置Mapper类
        job.setMapperClass(WordCountMapper.class);
        // 设置map任务输出键类型
        job.setMapOutputKeyClass(LongWritable.class);
        // 设置map任务输出值类型
        job.setMapOutputValueClass(Text.class);

        // 定义uri字符串
        String uri = "hdfs://192.168.219.75:9000";
        // 创建输入目录
        Path inputPath = new Path(uri + "/wordcount/input");
        // 创建输出目录
        Path outputPath = new Path(uri + "/wordcount/output");

        // 获取文件系统
        FileSystem fs =  FileSystem.get(new URI(uri), conf);
        // 删除输出目录(第二个参数设置是否递归)
        fs.delete(outputPath, true);

        // 给作业添加输入目录(允许多个)
        FileInputFormat.addInputPath(job, inputPath);
        // 给作业设置输出目录(只能一个)
        FileOutputFormat.setOutputPath(job, outputPath);

        // 等待作业完成
        job.waitForCompletion(true);

        // 输出统计结果
        System.out.println("======统计结果======");
        FileStatus[] fileStatuses = fs.listStatus(outputPath);
        for (int i = 1; i < fileStatuses.length; i++) {
            // 输出结果文件路径
            System.out.println(fileStatuses[i].getPath());
            // 获取文件系统数据字节输入流
            FSDataInputStream in = fs.open(fileStatuses[i].getPath());
            // 将结果文件显示在控制台
            IOUtils.copyBytes(in, System.out, 4096, false);
        }
    }
}
  •  注意导包问题

7、运行词频统计驱动器类,查看结果 

  • 注意:如果运行出现错误:Did not find winutils.exe

  • 请前往博客:

(8条消息) 大数据讲课笔记5.1 初探MapReduce_howard2005的博客-CSDN博客_mapreduce统计文件行数https://blog.csdn.net/howard2005/article/details/127218169?app_version=5.11.1&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22127218169%22%2C%22source%22%3A%22howard2005%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app

  • 最后的解决问题来解决
  • 再次运行,统计结果之前会显示大量信息
  • 如果不想看到统计结果之前的大堆信息,可以修改log4j.properties文件,将INFO改为ERROR

  • 再运行程序,查看结果

  • 行首数字,表示每行起始位置在整个文件的偏移量(offset)。
  • 第一行:hello hadoop world\n 16个字母,2个空格,1个转义字符,总共19个字符,因此,第二行起始位置在整个文件的偏移量就是19。
  • 第二行:hello hive world\n 14个字母,2个空格,1个转义字符,总共17个字符,因此,第三行起始位置在整个文件的偏移量就是19 + 16 = 36。
  • 第三行:hello hbase world\n 15个字母,2个空格,1个转义字符,总共18个字符,因此,第三行起始位置在整个文件的偏移量就是19 + 16 + 18 = 54。
  • 第四行:hadoop hive hbase\n 15个字母,2个空格,1个转义字符,总共18个字符,因此,第三行起始位置在整个文件的偏移量就是19 + 16 + 18 + 18 = 72。
  •  利用Hadoop WebUI界面查看结果文件

8、修改词频统计映射器类 

  • 行首数字对于我们做单词统计没有任何用处,只需要拿到每一行内容,按空格拆分成单词,每个单词计数1,因此,WordCoutMapper的输出应该是单词个数,于是,输出键类型为Text,输出值类型为IntWritable
  • 将每行按空格拆分成单词数组,输出<单词, 1>的键值对

  • 由于WordCountMapper输出键值类型发生变化,所以WordCountDriver也需要修改

9、修改词频统计驱动器类

  • 修改map任务输出键值类型


 

10、启动词频统计驱动器类,查看结果 

  • 观察输出结果,map阶段会按键排序输出

  • 映射任务与归并任务示意图

 11、创建词频统计归并器类

  • 一个类继承Reducer,变成一个Reducer组件类
  • Reducer组件会接收Mapper组件的输出结果
  • 第一个泛型对应的是Mapper输出key类型
  • 第二个泛型对应的是Mapper输出value类型
  • 第三个泛型和第四个泛型是Reducer的输出key类型和输出value类型
  • Reducer组件不能单独存在,但是Mapper组件可以单独存在
  • 当引入Reducer组件后,输出结果文件内容就是Reducer的输出key和输出value
  • 在net.rzh.mr包里创建WordCountReducer
     
package net.rzh.mr;

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

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class WordCountReducer extends Reducer<Text, IntWritable, Text, Text> {
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context)
            throws IOException, InterruptedException {
        // 定义整数数组列表
        List<Integer> integers = new ArrayList<>();
        // 遍历输入值迭代器
        for (IntWritable value : values) {
            // 将每个值添加到数组列表
            integers.add(value.get()); // 利用get()方法将hadoop数据类型转换成java数据类型
        }
        // 输出新的键值对,注意要将java字符串转换成hadoop的text类型
        context.write(key, new Text(integers.toString()));
    }
}

  • 创建了词频统计归并器之后,我们一定要去修改词频统计驱动器类

12、修改词频统计驱动器类 

  • 设置词频统计的Reducer类及其输出键类型和输出值类型(Text,Text)

 13、运行词频统计驱动器类,查看结果

  • 运行WordCountDriver类,查看结果

  •  现在我们需要修改词频统计归并器,将每个键(单词)的值迭代器进行累加,得到每个单词出现的总次数。

14、修改词频统计归并器类

  • 输出键值类型改为IntWritable,遍历值迭代器,累加得到单词出现次数

package net.rzh.mr;

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> {
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context)
            throws IOException, InterruptedException {
        // 定义键出现次数
        int count = 0;
        // 遍历输入值迭代器
        for (IntWritable value : values) {
            count += value.get(); // 其实针对此案例,可用count++来处理
        }
        // 输出新的键值对,注意要将java的int类型转换成hadoop的IntWritable类型
        context.write(key, new IntWritable(count));
    }
}

  • 由于修改了词频统计归并器的输出值类型(由Text类型改成了IntWritable类型),必须在词频统计驱动器类里进行设置

15、修改词频统计驱动器类 

  • 修改归并任务的输出值类型(IntWritable类型)

 16、启动词频统计驱动器类,查看结果

  • 此时,可以看到每个单词出现的次数

  •  知识点:

(1)MR框架有两个核心组件,分别是Mapper组件和Reducer组件
(2)写一个类,继承Mapper,则变成了一个Mapper组件类
(3)IntWritable, LongWritable,DoubleWritable, Text,NullWritable都是Hadoop序列化类型
(4)Mapper组件将每行的行首偏移量,作为输入key,通过map()传给程序员
(5)Mapper组件会将每行内容,作为输入value,通过map()传给程序员,重点是获取输入value
(6)Mapper的第一个泛型类型对应的是输入key的类型,第二个泛型类型对应的输入value
(7)MR框架所处理的文件可以是本地文件,也可以是HDFS文件
(8)map()被调用几次,取决于文件的行数
(9)通过context进行结果的输出,以输出key和输出value的形式来输出
(10)输出key是由第三个泛型类型决定,输出value是由第四个泛型类型决定
(11)输出结果文件的数据以及行数取决于context.write()方法
(12)Text => String:  value.toString()
(13)String => Text:  new Text(strVar)
(14)LongWritable => long:  key.get()
(15)long => LongWritable: new LongWritable(longVar)
 

17、采用多个Reduce做合并

  • 相同key的键值对必须发送同一分区(一个Reduce任务对应一个分区,然后会生成对应的一个结果文件,有多少个Reduce任务,就会有多少个分区,最终就会产生多少个结果文件),否则同一个key最终会出现在不同的结果文件中,那显然不是我们希望看到的结果。

17.1、MR默认采用哈希分区HashPartitioner

  • Mapper输出key.hashcode & Integer.MAX_ VALUE % Reduce任务数量 

17.2、修改词频统计驱动器类,设置分区数量 

  • 设置分区数量:3

  • 此时,运行程序,查看结果

  • 在Hadoop WebUI界面上可以看到,产生了三个结果文件

五、课后复习

  • 特别需要注意导包问题,如果导包不正确,运行程序会出错,课后多看一下代码,了解各行代码的作用
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值