MapReduce基础原理及应用

本文介绍了MapReduce的基本原理,包括其在大规模数据处理中的应用、计算模型及其实现细节。探讨了MapReduce的两个核心阶段:Map和Reduce,并解释了如何通过编写Map和Reduce函数来实现数据处理任务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MapReduce基础原理:

MapReduce(起源于Google):

MapReduce是一种计算模型,它将大型数据操作作业分解为可以跨服务器集群并行执行的单个任务。用于管理DataNode

用于大规模数据处理:每个节点处理存储在该节点上的数据

每个MapReduce工作由两个阶段组成:Map;Reduce

自动MapReduce计算:

MapReduce计算是并行和自动分布的

开发人员只需要专注于实现映射和reduce功能

M/R可以用Java编写的,但也支持Python流

使用MapReduce的原因:

MapReduce是一种模式,适合解决并行计算的问题,比如TopN、贝叶斯分类等;而非迭代计算,如涉及到层次聚类的问题就不太适合了。

这种模式有两个步骤,Map和Reduce。Map即数据的映射,用于把一组键值对映射成另一组新的键值对; Reduc以Map阶段的输出结果作为输入,对数据做化简,合并等操作。

MapReduce是Hadoop生态系统中基于底层HDFS的一个计算框架,它的上层又可以是Hive、Pig等数据仓库框架,也可以是Mahout这样的数据挖掘工具。

由于MapReduce依赖于HDFS,其运算过程中的数据等会保存到HDFS上,把对数据集的计算分发给各个节点,并将结果进行汇总,再加上各种状态汇报、心跳汇报等,其只适合做离线计算。MapReduce和实时计算框架Storm、Spark等相比,速度上没有优势。

Hadoop v1生态几乎是以MapReduce为核心的,但是随着发展,其逐渐出现扩展性差、资源利用率低、可靠性等问题,于是产生了Yarn;所以Hadoop v2生态都是以Yarn为核心。Storm、Spark等都可以基于Yarn使用。

怎么运行MapReduce

在写完代码后,打包成jar,在HDFS的客户端运行:yarn jar hadoop-1.0-SNAPSHOT.jar WordCount /little_prince.txt /user/bda/output即可。当然,也可以在IDE(如IDEA)中,进行远程运行、调试程序。

如何编写MapReduce程序:

MapReduce中有Map和Reduce,在实现MapReduce的过程中,主要分为这两个阶段,分别以两类函数进行展现,一个是map函数,一个是reduce函数。map函数的参数是一个<key,value>键值对,其输出结果也是键值对,reduce函数以map的输出作为输入进行处理。

Map类:

创建Map类和map函数,map函数是org.apache.hadoop.mapreduce.Mapper类中的定义的,当处理每一个键值对的时候,都要调用一次map方法,用户需要覆写此方法。此外还有setup方法和cleanup方法。map方法是当map任务开始运行的时候调用一次,cleanup方法是整个map任务结束的时候运行一次。

Map介绍:

Mapper类是一个泛型类,带有4个参数(输入的键,输入的值,输出的键,输出的值)。在这里输入的key为Object(默认是行),输入的值为Text(hadoop中的String类型),输出的key为Text(关键字)和输出的值为IntWritable(hadoop中的int类型)。以上所有hadoop数据类型和java的数据类型都很相像,除了它们是针对网络序列化而做的特殊优化。

MapReduce中的类似于IntWritable的类型还有如下几种:

BooleanWritable:标准布尔型数值、ByteWritable:单字节数值、DoubleWritable:双字节数值、FloatWritable:浮点数、IntWritable:整型数、LongWritable:长整型数、Text:使用UTF8格式存储的文本(类似java中的String)、NullWritable:当<key, value>中的key或value为空时使用。

这些都是实现了WritableComparable接口:

·

Map任务是一类将输入记录集转换为中间格式记录集的独立任务。 Mapper类中的map方法将输入键值对(key/value pair)映射到一组中间格式的键值对集合。这种转换的中间格式记录集不需要与输入记录集的类型一致。一个给定的输入键值对可以映射成0个或多个输出键值对。

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

while (itr.hasMoreTokens()) {

word.set(itr.nextToken());

context.write(word, one);

}

这里将输入的行进行解析分割之后,利用Context的write方法进行保存。而Context是实现了MapContext接口的一个抽象内部类。此处把解析出的每个单词作为key,将整形1作为对应的value,表示此单词出现了一次。map就是一个分的过程,reduce就是合的过程。Map任务的个数和前面的split的数目对应,作为map函数的输入。Map任务的具体执行见下一小节。

 

代码构成:

实际的代码中,需要三个元素,分别是Map、Reduce、运行任务的代码。这里的Map类是继承了org.apache.hadoop.mapreduce.Mapper,并实现其中的map方法;而Reduce类是继承了org.apache.hadoop.mapreduce.Reducer,实现其中的reduce方法。至于运行任务的代码,就是我们程序的入口。

MapReduce计算单词数量:

先进性分割分开一段一段的用物理块分开,先进行分词处理,第一个Map进行全文统计,进行词频统计分开成为key-value对的形式,每遇到一个词就形成一个键值对,输出计算结果联合情况,先进行局部统计,再进行全局统计进行数据迁移和排序,最后送给Reduce进行最后统计。

Shuffle数据迁移,很慢需要很多时间,要尽量避免,或者缩短时间,shuffle是一个临界点前面是Map后面是Reduce

局部统计的好处:如果没有Combine送到Shuffle和Reduce的数量就太多了,减少迁移数据量可以加快处理速度,应该把更多的工作放在Map里

Text是Hadoop创造的一个类

StringTokenizer是指定使用分词的方法进行计算

将想要计数的文本文件放进Hadoop中,编写的程序直接到虚拟机的yarn中运行

 

计算的原则:

  1. 尽量减少数据迁移
  2. 尽量提升并行处理
  3. 都是在DataNode里做,NameNode从来不做这些操作

 

算法:解决问题的方法和步骤

不可分割,可以执行,算法描述有文字版、图形版、代码版以及伪代码版

查找算法的时间复杂度,时间和空间之间的关系

常数复杂度,线性复杂度,指数复杂度

 

MapReduce框架:

Mapper一定要有:

Combiner可有可无:只能用在交换率(a.b = b.a)和结合律{a.(b.c) =(a.b).c},比如在计算所有人平均年龄的时候不能使用combiner进行除法,只能进行加减法,因为使用除法时候不适用结合律

Partitioner一定要有:分区处理器,判断哪个key-value送到哪个Reduce里,把不同的key-value送到不同的Reduce里,默认值是HashPartitioner中的key是用hashCode拿到的是整形的,有几个Reduce就进行%几的计算,余数是几就到哪个Reduce里,它对numOfPartitions执行一个模块化操作以返回分区号

Shuffle和Sort:Map到Reduce的过程是push的过程,要进行重新洗牌,并根据Reduce进行重新排序

Reducer在一定情况下可能没有:

InputSplit是逻辑块,只是一个概念:

HDFS切割时候不是用逻辑块切分,使用的是物理块,而InputSplit就是用逻辑块分割,一般逻辑块都比物理块数量多,在记录的边界处划分

Mapper的数量是由InputSplit决定的,一定要订了逻辑结构和物理大小才能决定Mapper的数量

InputSplit表示要由单个Mapper处理的数据:

块是数据的物理表示。分割是块中数据的逻辑表示。

InputSplit涉及逻辑记录边界:

在MapReduce执行期间,Hadoop扫描这些块,并创建inputsplit并且每个InputSplit将分配给各个映射器进行处理。

MapReduce编程模型:

Map必须有,拿到的值是key-value;Combiner可有可无;某些MapReduce没有Reduce,比如往HDFS里写文件就没有Reduce,只要分块写就好,

如果想指定Map的个数,就要在文件送进HDFS中时规定好文件的大小

InputFormat是一个接口:调用getInputSplit方法知道有几个InputSplit,计算得到需要启动几个Map,在数据所在地产生Map的实例。

InputFormat再调用create RecordReader里的read方法,从里面读取数据送给K-V。

OutputFormat调用write方法把K-V写出去,数据格式会保留;用一个check进行一个校验,实现接口把他穿进去

Key和Value的类型都是可序列化的

因为Object是不可以通过网络传递的,而我们计算时要通过网络进行传输,所以key-value一定是可序列化的

利用Hadoop基于可写的序列化机制向网络进行写入和写出数据的操作

优化的网络序列化

能够通过重用可写对象来减少对象创建开销

提供了一组基本类型:

IntWritable,LongWriteable,FloatWritable,DoubleWritable,BooleanWritable,Text,NullWritable(没有内容要写出的时候使用NullWritable)等等

易于创建自定义Key-Value类型

Values必须继承Writable接口:

void readFields(DataInput in)——反序列化字段

void write(DataOutput out)——序列化字段

Key必须实现WritableComparable接口:

Key是排序优先减少阶段,因为有排序所以Key一定是可比较的

void readFields(DataInput in)——反序列化字段

void write(DataOutput out)——序列化字段

int compareTo(T T)——将该对象与指定对象进行比较

int hashCode()——分区程序用于决定发送哪个还原程序的键-值对。

InputFormat接口:

InputFormat定义如何将数据从输入读入Mapper实例。

InputSplit[] getSplits(JobConf job, int numSplits) throws IOException

然后将每个InputSplit分配给一个单独的Mapper进行处理

RecordReader<K,V> getRecordReader(InputSplit split, JobConf job,Reporter reporter) throws IOException

获取给定InputSplit的RecordReader

RecordReader有责任在处理逻辑拆分时遵守记录边界,以便向单个工作提供record-oriented视图。

输入格式:

TextInputFormat使用最多,NLineInputFormat是读取多行记录,

Mapper类:

protected void setup(org.apache.hadoop.mapreduce.Mapper.Context context) throws IOException, InterruptedException

初始化在任务开始时调用一次,mapreduce框架调用,可以进行一些设置。

protected void map(KEY key, VALUE value, Context context) throws IOException, InterruptedException

对输入分割中的每个键/值对调用一次。大多数应用程序都应该覆盖它,但是默认情况下是同一的函数

protected void cleanup(Context context) throws IOException, InterruptedException

在任务结束时调用一次,清除

public void run(Context context) throws IOException, InterruptedException

专家用户可以重写此方法,以便更完整地控制Mapper的执行,可以进行处理

示例:

public class WCMapper extends Mapper<LongWritable, Text, Text, IntWritable>{

// LongWritable, Text是输入,Text, IntWritable是输出

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

    //每次多一个对象就是1

    private Text word = new Text();

    //提前创造好对象

    public void map(LongWritable key, Text value, Context ctx) throws IOException, InterruptedException{

    // LongWritable是输入Key,Text是输入value,Context是和Reduce框架进行沟通的

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

        //把文本分成一行一行的集合

        while ( itr.hasMoreTokens() ){

            word.set( itr.nextToken() );

            //判断如果有下一个Token就设置进Text中并且设一个值为1

            ctx.write( word, one);   }   }   }

Combiner(可选的):

Combiner是MapReduce中的一种优化,它允许在洗牌和排序阶段之前进行本地聚合。

Combiner只能用于以下函数:

交换律:a.b = b.a

结合律:a.(b.c) = (a.b).c

Combiner与Reducer具有相同的接口,因此在大多数情况下只要满足2号要求,Reducers就可以作为Combiner使用。

job.setCombinerClass( WCReducer.class )

Partitioner类:

当Mapper输出被收集时,它将被分区,这意味着它将被写入Partitioner指定的输出

Partitioners负责划分中间键空间,并将中间键值对分配给reducers

默认的Partitioner包括计算键的哈希值,然后使用该值与reducers数量的模。

Reducer类:

protected void setup(org.apache.hadoop.mapreduce.Mapper.Context context) throws IOException, InterruptedException

在任务开始时调用一次。

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

为每个键调用一次。大多数应用程序都应该覆盖它,但是默认情况下是同一的函数

protected void cleanup(Context context) throws IOException, InterruptedException

在任务结束时调用一次,清除

public void run(.Context context) throws IOException, InterruptedException

高级应用程序编写人员可以使用run方法来控制reduce任务的工作方式

示例:

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

    private IntWritable result = new IntWritable();

    public void reduce(Text key, Iterable< IntWritable > values, Context ctx) throws IOException, InterruptedException {

        int sum = 0;

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

        //把所有的数据加进来

             result.set( sum );

            //把整数写进IntWirtable中,在写到OutputFormat里

            ctx.write( key, result );   }   }

输出格式:

连接一个M/R工作

Configuration cfg = new Configuration();

Job job = Job.getInstance(getConf(), "WordCountMR" );

//告诉jar包是哪一些

job.setJarByClass( getClass() );

//初始输入文件

FileInputFormat.setInputDirRecursive(job, true);

FileInputFormat.addInputPath(job,  new Path(args[0]));

//初始输入格式

job.setInputFormatClass( TextInputFormat.class );

//最终输出路径

FileOutputFormat.setOutputPath( job, new Path(args[1]) );

//最终输出格式

job.setOutputFormatClass( TextOutputFormat.class );

//map任务处理类

job.setMapperClass( WCMapper.class );

//map输出类型.

job.setMapOutputKeyClass( Text.class );

job.setMapOutputValueClass( IntWritable.class );

//reduce任务处理类

job.setReducerClass( WCReducer.class );

// reduce统一输出类型

job.setOutputKeyClass( Text.class );

job.setOutputValueClass( IntWritable.class );

执行M/R工作

yarn jar hadoop-1.0-SNAPSHOT.jar WordCount /little_prince.txt /user/bda/output

设置Mapper的数量

java WCMR.jar /user/data /user/out –D mapred.map.tasks=5

设置reducer的数量

yarn jar WCMR.jar /user/data /user/out –D mapred.reduce.tasks=2

Job.setNumReduceTasks( 3 );

MapReduce有两种方法:

Map-Side Join适用于一个大表和一个小表的情况A big table + a small table

Reduce-Side Join适用于两个大表的情况two tables are big

分布式缓存:

分布式缓存有助于在执行M/R作业时向Hadoop集群中的任务节点发送只读文件。

文件可以是查找表、文本文件、jar等

文件的大小可以很小

文件每个工作会复制一份

示例:

放文件:

Job job = new Job();

job.addCacheFile(new Path(filename).toUri());

job.addCacheFile(new URI("/user/data/customer_types.json#customer_type"));

取文件:三种方法取一种

URI[] files = context.getCacheFiles();

Path filename = new Path(files[0])

File customerType = new File("./customer_type");

DistributedCache类已被弃用

 

/user/data/exchange_ rate.csv

job.addCacheFile("/user/data/exchange_ rate.csv")

把/user/data/exchange_ rate.csv这个文件的内容发到 DataNode (exchage_ rate.csv)上

job.addCacheFile("/user/data/exchange_ rate.csv#er.csv")

把/user/data/exchange_ rate.csv这个文件内容发到DataNode (er.csv)上

DataNode . >

erFile:File=new File("./exchage_ rate.csv")

erFile: File . new File(" ./er.csv")

Map-Side Join

a big table + a small table  1

small table:mapper -> setup ( erFile:File . new File("./er.csv") --> hashtable

big table:map( ... ) {

record(sc, dc) -> erFile }

 

预测执行(SE):

问题:当100个map任务中的99个已经完成时,系统仍然在等待最终的map任务签入,这比所有其他节点花费的时间要长得多。

解决:使用SE

相同的输入可以并行处理多次,以利用机器功能的差异。

将慢速任务的冗余副本安排在没有其他工作要执行的多个节点上。

任务最先完成的副本成为最终副本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值