mapreduce编程初探[通俗易懂]

mapreduce编程初探[通俗易懂]1.map和reduce1.1mapReduce处理逻辑在本系列文章的第一篇中,曾对MapReduce原理做过简单的描述,在这里再重述一遍。 首先我们有两个文件word1.txt和word2.txt 其中word1.txt的内容如下:aaaabbbbccccddddaaaaword2.txt的内容如下:aaaaccccddddeeeeaaaa这…

大家好,又见面了,我是你们的朋友全栈君。

1.map和reduce

1.1 mapReduce处理逻辑

在本系列文章的第一篇中,曾对MapReduce原理做过简单的描述,在这里再重述一遍。 
首先我们有两个文件word1.txt和word2.txt 
其中word1.txt的内容如下:

aaaa
bbbb
cccc
dddd
aaaa

word2.txt的内容如下:

aaaa
cccc
dddd
eeee
aaaa

这里的两个文件很小,我们先假设这两个文件很大,分别为64M和96M的大小,然后我们需要统计文件中每个字符串的数量,那么MapReduce的处理流程如下: 
这里写图片描述
Input:最左边是输入的过程,输入了图示的数据。 
Split分片:mapreduce会根据输入的文件计算分片,每个分片对应与一个map任务。而分片的过程和HDFS密切相关,比如HDFS的一个block大小为64M,我们输入的两个文件分比为64M,96M,这样的话第一个文件生成一个64M的分片,第二个文件生成一个64M的分片和一个32M的分片(如果有一个小于64M的文件,比如10M的文件,那么这个文件会生成一个单独的10M的分片) 
Map:map阶段是由编程人员通过代码来控制的,图中所示的大概内容就是将字符串分割开来,作为键存储在map中,值的位置存储1,表示数量。 
shuffle洗牌:洗牌阶段,由于之前生成map中存在很多键相同的map,在洗牌阶段将键相同的进行合并。 
Reduce:reduce阶段也是有开发人员通过代码控制,本例中是将键相同的map的value值进行求和,得出最终的map 
这样最后输出的数据就是每个字符串出现的次数。

1.2 Hadoop数据类型

Hadoop本身提供了一套可优化网络序列化传输的基本类型

类型 含义
BooleanWritable 标准布尔型数值
ByteWritable 单字节数值
DoubleWritable 双字节数值
FloatWritable 浮点数
IntWritable 整型数
LongWritable 长整型数
Text 使用UTF8格式存储的文本
NullWritable 当中的key或value为空时使用

1.3 Mapper

Mapper类是一个泛型类,四个参数分别指定map函数的输入键,输入值,输出键,输出值 
这里写图片描述 
Mapper类包含四个方法: 
这里写图片描述 
setup方法在任务开始时调用一次,一般用来做map前的准备工作。 
map承担主要的处理工作,把输入数据拆分为键值对。 
cleanup方法则是在任务结束时调用一次,主要负责收尾工作。 
run方法确定了setup-map-cleanup的执行模板。 
map()方法的输入是一个键和一个值,输出是一个Context实例: 
这里写图片描述 
先了解到这里,后续我们结合代码来进一步了解Mapper。

1.4 Reducer

Reducer类也是一个泛型类,与Mapper相似,四个参数分别指定map函数的输入键,输入值,输出键,输出值 
这里写图片描述 
Reducer类也包含四个方法: 
这里写图片描述

setup方法在任务开始时调用一次,一般用来做reduce前的准备工作。 
reduce承担主要的处理工作,把输入数据拆分为键值对。 
cleanup方法则是在任务结束时调用一次,主要负责收尾工作。 
run方法确定了setup-reduce-cleanup的执行模板。

注意,Reducer的输入类型必须匹配Mapper的输出类型。

2.代码分析

接下来我们来看一下上一篇文章用到的测试代码:

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;
import org.apache.hadoop.util.GenericOptionsParser;

public class WordCount {
    //继承mapper接口,设置map的输入类型为<Object,Text>
    //输出类型为<Text,IntWritable>
    public static class Map extends Mapper<Object,Text,Text,IntWritable>{
        //one表示单词出现一次
        private static IntWritable one = new IntWritable(1);
        //word存储切下的单词
        private Text word = new Text();
        public void map(Object key,Text value,Context context) throws IOException,InterruptedException{
            //对输入的行切词
            StringTokenizer st = new StringTokenizer(value.toString());
            while(st.hasMoreTokens()){
                word.set(st.nextToken());//切下的单词存入word
                context.write(word, one);
            }
        }
    }
    //继承reducer接口,设置reduce的输入类型<Text,IntWritable>
    //输出类型为<Text,IntWritable>
    public static class Reduce extends Reducer<Text,IntWritable,Text,IntWritable>{
        //result记录单词的频数
        private static IntWritable result = new IntWritable();
        public void reduce(Text key,Iterable<IntWritable> values,Context context) throws IOException,InterruptedException{
            int sum = 0;
            //对获取的<key,value-list>计算value的和
            for(IntWritable val:values){
                sum += val.get();
            }
            //将频数设置到result
            result.set(sum);
            //收集结果
            context.write(key, result);
        }
    }
    /**
     * @param args
     */
    public static void main(String[] args) throws Exception{
        // TODO Auto-generated method stub
        Configuration conf = new Configuration();
        conf.set("mapred.job.tracker", "localhost:9001");
        args = new String[]{"hdfs://localhost:9000/user/hadoop/input/count_in","hdfs://localhost:9000/user/hadoop/output/count_out"};
        //检查运行命令
        String[] otherArgs = new GenericOptionsParser(conf,args).getRemainingArgs();
        if(otherArgs.length != 2){
            System.err.println("Usage WordCount <int> <out>");
            System.exit(2);
        }
        //配置作业名
        Job job = new Job(conf,"word count");
        //配置作业各个类
        job.setJarByClass(WordCount.class);
        job.setMapperClass(Map.class);
        job.setCombinerClass(Reduce.class);
        job.setReducerClass(Reduce.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
        FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }

}

WordCount类可以分为三部分,Map,Reduce和main三部分,Map和Reduce都是静态内部类。 
Map类继承与Mapper类,四个参数表示其输入键类型为Object,输入值为文本,输出键为文本,输出值为整型数。 
通过执行Map操作后,我们希望得到的结果是图1中第三列mapping列的值,即将数据拆分后存储到map中,每个字符串的数量均存储为1. 
在代码中定义了一个整型类型的变量one,值为1,用来作为map的值。 
map方法的前两个参数分别为输入的键和值,通过下面的代码先将text格式的字段转为java的String类型。

StringTokenizer st = new StringTokenizer(value.toString());

StringTokenizer 根据自定义字符为分界符对字符串进行拆分并将结果集封装提供对应的遍历方法,有如下构造方法: 
这里写图片描述 
str为要拆分的字符串,delim为界定符,当不指定delim时,将默认以空格进行拆分。

有如下方法: 
这里写图片描述 
其中hasMoreTokens方法用来判断是否还有分隔符。 
使用context的write方法将数据进行记录。

Reduce类继承于Reducer类,Reducer类是一个泛型类,四个参数分别表示输入键,输入值,输出键,输出值。其中输入键和输入值与Map类的输出键,输出值保持一致。 
当数据到达reduce时,数据已经经过了洗牌,即键相同的数据进行了合并,所以reduce方法的key为键,values是一个迭代器,存储着该键对应的所有值,然后在方法体中对该键对应的值得数量进行了统计。 
如果我们在map方法中分别写一句System.out.println(“map”)和System.out.println(“reduce”),就会发现map方法和reduce方法都不止被执行了一次。

main方法来控制任务的执行。 
要知道,使用MapReduce框架时,我们仅仅只是填写map和reduce部分的代码,其他的都交给mapreduce框架来处理,所以我们至少需要告诉mapreduce框架应该怎么执行,main方法中的代码做的就是这个操作。 
首先我们需要初始化Configuration类,使用MapReduce之前一定要初始化Configuration,该类主要用来读取hdfs和Mapreduce的配置信息。 
args设置输入文件和输出文件的位置,这里指向hdfs,输出文件的文件夹可以不存在,运行后会在指定目录下自动生成,输出文件一定不能存在,在运行前要将上一次运行生成的输出文件删除掉。 
在上面的代码中我们是通过下面的代码来配置的:

conf.set("mapred.job.tracker", "localhost:9001");

我们也可以将该信息添加到xml文件中来配置,如下图: 
这里写图片描述 
代码修改为: 
这里写图片描述 
接下来的if部分用来判断是否有两个参数都指定了。 
再往下就是配置作业。首先创建一个Job类,然后装载需要的各个类,从上到下分别为:程序类(我们编写的java文件的类名,这里是WordCount),Mapper类(继承了Mapper类的内部类,这里是Map), 
Combiner和Reducer类都指向继承于Reducer的内部类Reduce. 
需要特别注意的是,Combiner并非一定要指向Reducer类,有时候也可以不指定,有时候不能指向Reducer而是需要单独写Combiner,只是这里指向Reducer而已) 
再往下两行:

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

指定了输出数据的键和值的类型,也是数据存储到hdfs结果文件中的类型。 
下面的代码用来创建输入文件和输出文件:

FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));

最后一行代码表示执行成功后退出程序。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/148465.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 字符串的匹配算法_多字符串匹配

    字符串的匹配算法_多字符串匹配目录需求基础知识逻辑解析源码实现需求先简单描述溪源曾经遇到的需求:需求一:项目结果文件中实验结论可能会存在未知类型、转换错误、空指针、超过索引长度等等。这里是类比需求,用日常开发中常出现的错误类型作为需求,如果要以上结论则判断这个项目检测失败;解决方案一:大家常用的方式可能是if(){continue;}esleif(){continue;}…或者switch-case等;方案二:可能会使用集合contain()方法;方案三:依次匹配字符串中字符(暴力匹配);以上两种方案都能解决;然

  • mybatis SqlSessionFactoryBean SqlSessionTemplate MapperScannerConfigurer

    mybatis SqlSessionFactoryBean SqlSessionTemplate MapperScannerConfigurermybatis SqlSessionFactoryBean SqlSessionTemplateMapperScannerConfigurer1.MyBatis简介   MyBatis是什么?MyBatis是一款一流的支持自定义SQL、存储过程和高级映射的持久化框架。MyBatis几乎消除了所有的JDBC代码,也基本不需要手工去设置参数和获取检索结果。MyBatis能

  • 51单片机学习笔记:合并1602和12864液晶排插接口

    51单片机学习笔记:合并1602和12864液晶排插接口 今天成功合并1602和12864液晶排插接口! 码出来分享下 上面这2个图是1602和12864液晶的排插接口,一般的单片机开发板上都会有仔细观察发现他们的插口大多是相同的, 对于第三脚的对比度调节,1602和12864液晶在硬件上是相反的(1602是低电位方向对比度增强,12864是高电位方向对比度增强),但他们接口位置相同,所以一个10K左右的3脚电位器…

    2022年10月20日
  • 关于数据连接配置connectionStrings的写法[通俗易懂]

    关于数据连接配置connectionStrings的写法[通俗易懂]参考http://www.connectionstrings.com/1、SQLServer<addname="ApplicationName"connectionSt

  • canoe入门教程_canoe编程

    canoe入门教程_canoe编程转自:https://www.cnblogs.com/fengliu-/p/7844072.htmlCANOE入门(一)CANoe是Vector公司的针对汽车电子行业的总线分析工具,现在我用CANoe7.6版本进行介绍,其他版本功能基本差不多。硬件我使用的是CANcaseXL.1,CANoe软件的安装很简单,先装驱动,再装软件。安装完成,插上USB,连接硬件,这样在控制面板中,…

    2022年10月29日
  • 1. Git安装与配置

    1. Git安装与配置本文介绍Windows下的Git安装与配置

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号