mapreduce概念和案例(尚硅谷学习笔记)

MapReduce概念

概述

MapReduce是一个可用于大规模数据处理的分布式计算框架,具体包含以下三层含义:
1)MapReduce是一个并行程序的计算模型与方法
Mapreduce是一个编程模型,主要用来解决海量数据的并行计算。它借助函数式编程和“分而治之”的思想,使用户参照map和reduce两个思想描述清楚想要处理的问题。
2)MapReduce是一个并行程序运行的软件框架
用户只需要编写业务逻辑代码,MapReduce将其和自带默认组件整合成一个完整的分布式运算程序通过自带默认组件,自动完成计算任务的并行化处理,包括自动划分计算数据和计算任务,在集群上自动分配和执行任务以及手机计算结果等复杂细节问题。
3)MapReduce是一个基于集群的高性能并行计算平台
基于MapReduce框架编写出来的应用程序能够运行在上千个节点组成的集群上,以可靠的方式并行处理TB或者PB级别的数据量。

优点和缺点

优点
1)Mapreduce易于编程。
2)具有良好的扩展性
3)高容错性
4)适合PB级以上海量数据的离线处理
缺点
1)不适合实时计算
2)不适合流式计算
3)不适合DAG计算

可编程组件

InputFormat
从InputFormat类图看,InputFormat抽象类仅有两个抽象方法:

List getSplits(), 获取由输入文件计算出输入分片(InputSplit),解决数据或文件分割成片问题。
RecordReader<K,V> createRecordReader(),创建RecordReader,从InputSplit中读取数据,解决读取分片中数据问题。
总体来说,InputFormat提供了如下功能:

用于输入的文件或者对象是由InputFormat负责选取的。
将输入文件切割成逻辑分片(InputSplit),一个InputSplit将会被分配给一个独立的MapTask
提供RecordReader实现,读取InputSplit中的“K-V对”供Mapper使用
每欠切片时,都要判断切完剩下的部分是否大于块的1.1倍,不大于1.1倍就划分一块切片
将切片信息写到一个切片规划文件中
整个切片的核心过程在getSplitl方去中完成

FileInputFormat常见的接口实现类包括:TextInputFormatKeyValueTextInputFormatNLineInput FormatCombineTextInputFormat自定义InputFormat等。
1.TextInputFormat
TextInputFormat是默认的InputFormat。每条记录是一行输入。键是LongWritable类型,存储该行在整个文件中的起始字节偏移量。键是这行的内容,不包括任何行终止符(换行符和回车符)。
2.KeyValueTextInputFormat
针对文本文件,使用分割字符,分隔符前的为Key,分隔符后的为Value。如果没有找到分隔符,当前行的内容作为key,value为空串。
默认分隔符为\t,可以通过参数mapreduce.input.keyvaluelinerecordreader.key.value.seporator指定分隔符
切片:默认的切片策略,即一行切为一片

RecordReader:KeyValueLineRecordReader
3.NLineInputFormat
切片策略: 读取配置文件中的参数mapreduce.input.lineinputformat.linespermap,默认为1,以文件为单位,切片每此参数行作为1片。可以修改下列代码,设置为每N行切为一片:

Configuration conf = new Configuration();
conf.set("mapreduce.input.lineinputformat.linespermap", "N")

4.CombineTextInputFormat
切片机制:用于小文件过多的场景,它可以将多个小文件从逻辑上规划到一个切片中,多个小文件就可以交给一个 MapTask 处理
split
FileSplit有四个属性:文件路径,分片起始位置,分片长度和存储分片的hosts。用这四项数据,就可以计算出提供给每个Mapper的分片数据。在InputFormat的getSplit()方法中构造分片,分片的四个属性会通过调用FileSplit的Constructor设置。
FileSplit的问题:
FileSplit对应的是一个输入文件,就算一个输入文件特别小,它也会作为一个单独的InputSplit来处理,而每一个InputSplit将会由一个独立的Mapper Task来处理,这样的创建与销毁的开销非常巨大。
解决方法:
CombineFileSplit是针对小文件的分片,它将一系列小文件封装在一个InputSplit内,这样一个Mapper就可以处理多个小文件。可以有效的降低进程开销。与FileSplit类似,CombineFileSplit同样包含文件路径,分片起始位置,分片大小和分片数据所在的host列表四个属性,只不过这些属性不再是一个值,而是一个列表。
RecordReader
系统默认的RecordReader是LineRecordReaderTextInputFormat
LineRecordReader是用每行的偏移量作为map的key,每行的内容作为map的value
SequenceFileInputFormat的RecordReader是SequenceFileRecordReader

应用场景:自定义读取每一条记录的方式;自定义读入key的类型,如希望读取的key是文件的路径或名字而不是该行在文件中的偏移量。
Map
常用子类
HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
常用方法
public V put(K key, V value): 把指定的键与指定的值添加到Map集合中。
public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
boolean containsKey(Object key) 判断集合中是否包含指定的键。
public Set keySet(): 获取Map集合中所有的键,存储到Set集合中。
public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。
Partition
该该主要在Shuffle过程中按照Key值将中间结果分成R份,其中每份都有一个Reduce去负责,可以通过job.setPartitionerClass()方法进行设置,默认的使用hashPartitioner类。实现getPartition函数
Sort
MapTask和ReduceTask均会对数据按照key进行排序,这是hadoop的默认行为。任何应用程序中的数据均会被排序,不管他们是否需要。
如果key是封装为IntWritable类型,那么MapReduce按照数字大小对key进行排序。
如果key是封装为String的Text类型,那么MapReduce按照字典顺序对字符串排序。
对于MapTask,它会将处理的结果暂时放到环形缓冲区中,当环形缓冲区使用率达到一定阈值后,再对缓冲区中的数据进行一次快速排序,并将这些有序数据溢写到磁盘上,而当数据处理完毕后,它会对磁盘上所有文件进行归并排序。
对于ReduceTask,它从每个MapTask上远程拷贝相应的数据文件,如果文件大小超过一定阈值,则溢写磁盘上,否则存储在内存中。如果磁盘上文件数目达到一定阈值,则进行一次归并排序以生成一个更大文件;如果内存中文件大小或者数目超过一定阈值,则进行一次合并后将数据溢写到磁盘上。当所有数据拷贝完毕后,ReduceTask统一对内存和磁盘上的所有数据进行一次归并排序。

Combiner
combiner组件的父类是reducer
当所有数据处理完后,MapTask对所有临时文件进行一次合并,以确保最终会生成一个数据文件。
当所有数据处理完后,MapTask会将所有临时文件合并成一个大文件,并保存到文件output/file.out中,同时生成相应的索引文件output/file.out.index。进行文件合并过程中,MapTask以分区为单位进行合并。
对于某个分区,它将采用多轮递归合并的方式,每轮合并io.sort.factor(默认10)个文件,并将产生的文件重新加入到待合并列表中,对文件排序后,重复以上过程,直到最终得到一个大文件。
让每个MapTask最终只生 成一个数据文件,可避免同时打开大量文件和同时读取大量小文件产生的随机读取带来的开销。
combiner能够应用的前提是不影响最终的业务逻辑。同时combiner的输出kv需要和reducer的输入
Group
当整个数据处理结束之后,会对磁盘中所有的临时文件进行归并(把相同的key合并到一起),生成一个大文件
此时的归并是将所有临时文件中的相同分区的合并到一起,并对各个分区中的数据再进行一次排序
如何比较两个key是否相同
第一种是key的compareTo方法,返回0时认为两个key相同
第二种是设置一个自定义的GroupingComparatorClass,然后重写类里compare方法,这个方法会覆盖掉key中的compareTo方法,从而作为比较key是否相同的方法。
Reduce
在分组完成后,只需要在每一个分组当中将那些来源于不同文件的记录(在Map阶段已经打标志)分开,最后进行合并就ok了。
combiner和reducer的区别在于运行位置:
Combiner是在每一个MapTask所在的节点运行
Reducer是接收全局所有Mapper的输出结果

OutputFormat
OutputFormat接口主要用于描述输出数据的格式,它能够将用户提供的key/value对写入特定格式的文件中。其功能与前面描述的InputFormat相似,Hadoop提供的OutputFormat实例会把文件写在本地磁盘或HDFS上。每一个reducer会把结果输出写在公共文件夹中一个单独的文件内。这些文件一般命名为:part-xxxxx,而xxxxx是关联到某个reduce任务的partition的id。
常见的outputFormat实现类
1.文本输出TextOutputFormat
默认的输出格式是TextOutputFormat,它把每条记录写为文本行。它的键和值可以是任意类型,因为TextOutputFormat调用tostring()方法把它们转换为字符串。
2. SequenceFileoutputFormat
将SequenceFileOutputFormat输出作为后续MapReduce任务的输入,这便是一种好的输出格式,因为它的格式紧凑,很容易被压缩。
3.自定义OutputFormat
根据用户需求,自定义实现输出。

Wordcount案例

需求分析

我们希望能够获取到各学历阶段的人数情况(第四列)
在这里插入图片描述
mapper
1.将maptask传输的文本内容转换成string

30,Private,196456,Masters,14,Never-married,Prof-specialty,Not-in-family,White,Female,0,0,31,United-States,<=50K

2.根据,将这一行拆分成单词/词组,并且将每行第四个单词/词组设置为key

3.将key输出为<key,1>
reducer
1.汇总key的个数
2.输出key的总次数
driver
1.获取配置信息,获取job对象实例
2.指定本程序的jar包所在本地路径
3.关联mapper与reducer业务类
4.指定mapper输出数据的kv类型
4.指定最终输出数据的kv类型
6.指定job的输入原始文件所在目录以及输出路径目录
结果展示
在这里插入图片描述

代码实现

mapper类

在不确定有没有空字段的情况下,通过过滤避免统计结果出现偏差

package com.heria.mapreduce.startstation.huizong;

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阶段
//KEYIN输入数据的value
//KEYOUT输出数据的key类型
//VALUEOUT 输出的value类型
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>
{
   
    Text k = new Text();
    IntWritable v = new IntWritable(1);
    protected void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {
   
        // 1 获取一行
        String line = value.toString();
        // 2 切割
        String[] words = line.split(",");

        // 3 输出

        if(words.length>=3){
   
            if (words[3]!=null){
   
                String x=words[3];
                k.set(x);

                context.write(k, v);
            }
        }



    }
}

reducer类

package bigdata.startstation.huizong;

import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable>{
   
    int sum;
    IntWritable v = new IntWritable();
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException, InterruptedException {
   
        sum=0;
        //1 累加求和
        for(IntWritable value : values) {
   
            sum+= value.get();
        }
        //2 写出
        v.set(sum);

        context.write(key,v);
    }



}

driver类

package com.heria.mapreduce.startstation.huizong;

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
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值