mapreduce

mapreduce概述

mr是负责计算的。

定义: mapreduce是一个分布式运算程序的编程框架;是给予hadoop的数据分析应用的核心框架。
分布式是多个节点干同一件事情。

优点:

  1. 易于编程;简单的实现一些接口就能完成一个分布式的任务。
  2. 良好的扩展性;通过增加机器就能提高性能。
  3. 高容错性;其中一台挂了, 会自动的转移到另一个节点上运行,不需要人为操作。
  4. 适合pb级别的海量离线数据处理。
    缺点:
  5. 不是实时计算;不能像mysql一样在毫秒或者秒内返回结果。
  6. 不擅长流式计算。
    流式计算输入的数据是动态的;mr输入的数据是静态的;
  7. 不擅长有向图(DGA)计算
    有向图指的是多个程序存在依赖关系;后一个程序输入是前一个程序的输出;mr其实也能做,但是输出结果会造成大量磁盘io,导致性能下降;

map阶段:分
reduce:合

物理分区 逻辑分区

mr的核心编程思想
在这里插入图片描述
需求: 统计其中每一个单词传的总次数,查询结果a-p写到一个文件,q-z写到一个文件;

mapreduce运算程序一般分为2个阶段:map阶段和reduce阶段。map阶段是分;reduce阶段是合。
map阶段:
map阶段默认是按照块的大小,对数据进行分割,默认128M一个文件。假如是一个200M的文件, 会分成一个128M的和72M的文件。
处理一个数据块的时候会启动一个进程, 即MapTask任务。
MapTask任务是完全并行的。

处理数据的时候是启动MapTask任务,MapTask是并行运行,互不相干。

MapTask任务所做的工作:

  1. 读数据,按行处理
  2. 按空格切分每行的单词
  3. 切出来的结果是按照键值对的形式存储;比如<hadoop, 2> <spark,1> 前面的事键, 后面的是出现的次数 ,方便后面reduce阶段的统计。
  4. 将所有的键值对中的单词,按照单词首字母,分成2个分区溢写到磁盘

MapTask是按照实际的需求,进行任务操作。 MapTask任务完成之后,进入reduce阶段。
reduce阶段并发执行ReduceTask,但是数据依赖于上一个阶段的MapTask并行实际的输出结果。

ReduceTask任务所在的工作:

  1. 把MapTask任务的输出拿过来合并。
  2. 合并之后再进行输出;

mr编程模型只能包含一个map阶段和一个reduce阶段;如果用户业务逻辑复杂, 那只能是多个mr任务串行,第一个mr任务结束的输出作为下一个mr任务的输入。

一个MR任务在分布式运行的时候需要三个实例进程:

  1. MrAppMaster: 负责整个程序的资源调度;比如内存, cpu, 如何开启maptask和 reducetask ,是一个job的老大。
  2. Maptask: 负责map阶段的数据处理,就是分;
  3. Reducetask: 负责reduce阶段的数据处理,就是合;

hadoop数据类型和java数据类型

javahadoop
intIntWirteble
longLongWirteble
floatFloatWritable
StringText
doubleDoubleWritable
byteByteWritable
booleanBooleanWritable
nullNullWirteble

MR任务编程规范

map阶段:
  1. 用户自定义的mapper要继承自己的父类
  2. mapper的输入数据是键值对的形式,键值对的类型 可自定义
  3. mapper的业务逻辑写在map方法中
  4. mapper的输出是键值对形式
  5. map方法(maptask进程)对每一个键值对(<K, V>)调用一次
reduce阶段:
  1. 用户自定义的reducer要继承自己的父类
  2. reduce的输入数据是mapper的输出数据类型,也是KV,键值对
  3. reduce的业务逻辑写在reduce方法中
  4. reducetask进程对每一组相同的k的(<K, V>)组调用一次reduce方法
driver阶段:

相当于yarn集群的客户端,会将整个程序提交到yarn集群。提交的是封装了mapreduce程序相关运行参数的job对象。

junit 单元测试包

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.hadoop.mapreduce;

import java.io.IOException;
import org.apache.hadoop.classification.InterfaceAudience.Public;
import org.apache.hadoop.classification.InterfaceStability.Stable;

@Public
@Stable
public class Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {
    public Mapper() {
    }
  // 在开始的时候调用一次 初始化
    protected void setup(Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>.Context context) throws IOException, InterruptedException {
    }
	
	//每个键值对都会被调用一次,大多数的任务都要重写这方法。 必须重写的方法。
    protected void map(KEYIN key, VALUEIN value, Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>.Context context) throws IOException, InterruptedException {
        context.write(key, value);
    }
		// 任务结束的时候调用一次
    protected void cleanup(Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>.Context context) throws IOException, InterruptedException {
    }

    public void run(Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>.Context context) throws IOException, InterruptedException {
        this.setup(context);

        try {
            while(context.nextKeyValue()) {
                this.map(context.getCurrentKey(), context.getCurrentValue(), context);
            }
        } finally {
            this.cleanup(context);
        }

    }
	 // 上下文方法
    public abstract class Context implements MapContext<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {
        public Context() {
        }
    }
}

reduce的源码和map的差不多。

代码示例:

package demo1;

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;


//map阶段
//自己创建类,并且继承Mapper类,
// Mapper类有4个参数, 形式是这样的 Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
//四个参数的含义
// KEYIN, 输入数据的key
// VALUEIN, 输入数据的value
// KEYOUT, 输出数据的key的类型,
// VALUEOUT 输出数据的value类型



public class demo_2 extends Mapper<LongWritable, Text, Text, IntWritable>{
//    Text k = new Text();
//    IntWritable v = new IntWritable(1);  // m默认设置为 1
//    // LongWritable 代表行的偏移量
//    protected void map(LongWritable key, Text value, Context context)
//            throws IOException, InterruptedException{
//
        1. 获取一行, 将其转为字符串 哪怕拿到的可能已经是个字符串了
//        String line = value.toString();
//
        2. 切分单词 按空格
//        String[] words = line.split(" ");
//
        3. 循环写出
//        for(String word: words){
            创建对象 并将其设置
//            k.set(word);
            将value设置为1
//            context.write(k, v);
//        }

//    }

    Text k = new Text();
    IntWritable v = new IntWritable(1);
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String line = value.toString();

        String[] words = line.split(" ");

        for(String word:words){
            k.set(word);
            context.write(k, v);
        }

    }
}

package demo1;

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

import java.io.IOException;


//继承Reducer父类  形式是 Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
//KEYIN,  reduce的输入  是map的输出的
// VALUEIN,
// KEYOUT,
// VALUEOUT

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


    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context)
            throws IOException, InterruptedException {
                int sum = 0;

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

//                sum是int类型, 通过创建IntWritable对象,并使用set方法,将sum转为IntWritable类型
                IntWritable v = new IntWritable();
                v.set(sum);

                context.write(key, v);
    }
}

package demo1;

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 java.io.IOException;

public class demo_4 {
    public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException{
//        1. 获取job对象
         Configuration conf = new Configuration();
        Job job = Job.getInstance(conf);

//        到这一步,下面都是对job的封装,提交的就是封装后的job。


//        2. 设置jar存储的位置
//         有2种方式设置
//        1.
//              job.setJar(String jar);    这样写 就是将jar的位置写死, 不灵活
//        2.
//             job.setJarByClass(demo_4.class);   传入的参数是当前的类名, 原理是通过反射找到包的位置。
          job.setJarByClass(demo_4.class);

//        3. 关联map类和reduce类
        job.setMapperClass(demo_2.class);   // 调用的是 setMapperClass(Class<? extends Mapper> 类 reduce同理。
        job.setReducerClass(demo_3.class);

//        4. 设置mapper阶段的输出数据的key 和value类型
            job.setMapOutputKeyClass(Text.class);   //  setMapOutputKeyClass(Class<?> theClass)
            job.setMapOutputValueClass(IntWritable.class);  //  setMapOutputValueClass(Class<?> theClass)

//        5. 设置最终数据输出的key 和value的类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

//        6. 设置输入的路径 和输出的路径
        FileInputFormat.setInputPaths(job, new Path(args[0]));
//        FileInputFormat.setOutputPaths(job, new Path(args[1]))  这里本该有setOutputPaths 方法, 但是没出来, 不确定是不是调用的包的问题。

//        7. 提交job
//        job.submit(); 这样的也行
        boolean result = job.waitForCompletion(true);  // 使用这个方式, 如果为true会打印一些信息。
        System.exit(result ? 0 : 1);

//

//

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值