#博学谷IT学习技术支持#
1. 初识MapReduce
1.1 理解MapReduce思想
MapReduce思想在生活中处处可见,每个人或多或少都曾接触过这种思想。MapReduce的思想核心是“先分再合,分而治之”, 所谓“分而治之”就是把一个复杂的问题,按照一定的“分解”方法分为等价的规模较小的若干部分,然后逐个解决,分别找出各部分的结果,把各部分的结果组成整个问题的结果。
这种思想来源于日常生活与工作时的经验,同样也完全适用于大量复杂的任务处理场景(大规模数据处理场景)。即使是发布过论文实现分布式计算的谷歌也只是实现了这种思想,而不是自己原创。
Map负责“分”,即把复杂的任务分解为若干个“简单的任务”来并行处理。可以进行拆分的前提是这些小任务可以并行计算,彼此间几乎没有依赖关系。
Reduce负责“合”,即对map阶段的结果进行全局汇总。
这两个阶段合起来正是MapReduce思想的体现。
一个比较形象的语言解释MapReduce:
我们要数停车场中的所有的车数量。你数第一列,我数第二列。这就是“Map”。我们人越多,能够同时数车的人就越多,速度就越快。
数完之后,我们聚到一起,把所有人的统计数加在一起。这就是“Reduce”。
1.2场景:如何模拟实现分布式计算
什么是分布式计算
分布式计算是一种计算方法,和集中式计算是相对的。
随着计算技术的发展,有些应用需要非常巨大的计算能力才能完成,如果采用集中式计算,需要耗费相当长的时间来完成。
分布式计算将该应用分解成许多小的部分,分配给多台计算机进行处理。这样可以节约整体计算时间,大大提高计算效率。
大数据场景下模拟实现
1.3 Hadoop MapReduce设计构思
MapReduce是Hadoop的一个模块,是一个分布式运算程序的编程框架。
对许多开发者来说,自己完完全全实现一个并行计算程序难度太大,而MapReduce就是一种简化并行计算的编程模型,降低了开发并行应用的入门门槛。
Hadoop MapReduce构思体现在如下的三个方面。
如何对付大数据处理
对相互间不具有计算依赖关系的大数据计算任务,实现并行最自然的办法就是采取MapReduce分而治之的策略。
也就是Map阶段分的阶段,把大数据拆分成若干份小数据,多个程序同时并行计算产生中间结果;然后是Reduce聚合阶段,通过程序对并行的结果进行最终的汇总计算,得出最终的结果。
并行计算的第一个重要问题是如何划分计算任务或者计算数据以便对划分的子任务或数据块同时进行计算。不可分拆的计算任务或相互间有依赖关系的数据无法进行并行计算!
构建抽象模型
MapReduce借鉴了函数式语言中的思想,用Map和Reduce两个函数提供了高层的并行编程抽象模型。
Map: 对一组数据元素进行某种重复式的处理;
Reduce: 对Map的中间结果进行某种进一步的结果整理。
MapReduce中定义了如下的Map和Reduce两个抽象的编程接口,由用户去编程实现:
map: (k1; v1) → [(k2; v2)]
reduce: (k2; [v2]) → [(k3; v3)]
Map和Reduce为程序员提供了一个清晰的操作接口抽象描述。通过以上两个编程接口,大家可以看出MapReduce处理的数据类型是 <key,value>键值对。
统一架构、隐藏底层细节
如何提供统一的计算框架,如果没有统一封装底层细节,那么程序员则需要考虑诸如数据存储、划分、分发、结果收集、错误恢复等诸多细节;为此,MapReduce设计并提供了统一的计算框架,为程序员隐藏了绝大多数系统层面的处理细节。
MapReduce最大的亮点在于通过抽象模型和计算框架把需要做什么(what need to do) 与具体怎么做(how to do) 分开了,为程序员提供一个抽象和高层的编程接口和框架。
程序员仅需要关心其应用层的具体计算问题,仅需编写少量的处理应用本身计算问题的程序代码。如何具体完成这个并行计算任务所相关的诸多系统层细节被隐藏起来,交给计算框架去处理:从分布代码的执行,到大到数千小到单个节点集群的自动调度使用。
2. Hadoop MapReduce简介
2.1 MapReduce介绍
Hadoop MapReduce是一个软件框架,用于轻松编写应用程序,这些应用程序以可靠,容错的方式并行处理大型硬件集群(数千个节点)上的大量数据(多TB数据集)。
MapReduce是一种面向海量数据处理的一种指导思想,也是一种用于对大规模数据进行分布式计算的编程模型。
MapReduce最早由Google于2004年在一篇名为《MapReduce:Simplified Data Processingon Large Clusters》的论文中提出,把分布式数据处理的过程拆分为Map和Reduce两个操作函数(受到Lisp以及其他函数式编程语言的启发),随后被Apache Hadoop参考并作为开源版本提供支持。它的出现解决了人们在最初面临海量数据束手无策的问题,同时,它还是易于使用和高度可扩展的,使得开发者无需关系分布式系统底层的复杂性即可很容易的编写分布式数据处理程序,并在成千上万台普通的商用服务器中运行。
2.2 MapReduce特点
-
1.易于编程
- Mapreduce框架提供了用于二次开发得接口;简单地实现一些接口,就可以完成一个分布式程序。任务计算交给计算框架去处理,将分布式程序部署到hadoop集群上运行,集群节点可以扩展到成百上千个等。
-
2.良好的扩展性
- 当计算机资源不能得到满足的时候,可以通过增加机器来扩展它的计算能力。基于MapReduce的分布式计算得特点可以随节点数目增长保持近似于线性的增长,这个特点是MapReduce处理海量数据的关键,通过将计算节点增至几百或者几千可以很容易地处理数百TB甚至PB级别的离线数据。
-
3.高容错性
- Hadoop集群是分布式搭建和部署得,任何单一机器节点宕机了,它可以把上面的计算任务转移到另一个节点上运行,不影响整个作业任务得完成,过程完全是由Hadoop内部完成的。
-
4.适合海量数据的离线处理
- 可以处理GB、TB和PB级别得数据量
2.3 MapReduce局限性
MapReduce虽然有很多的优势,也有相对得局限性,不代表不能做,而是在有些场景下实现的效果比较差,并不适合用MapReduce来处理,主要表现在以下结果方面:
-
1.实时计算性能差
- MapReduce主要应用于离线作业,无法做到秒级或者是亚秒级得数据响应。
-
2.不能进行流式计算
- 流式计算特点是数据是源源不断得计算,并且数据是动态的;而MapReduce作为一个离线计算框架,主要是针对静态数据集得,数据是不能动态变化得。
3. Hadoop MapReduce编程
3.1 MapReduce架构体系
一个完整的mapreduce程序在分布式运行时有三类实例进程:
- MRAppMaster:负责整个程序的过程调度及状态协调
- MapTask:负责map阶段的整个数据处理流程
- ReduceTask:负责reduce阶段的整个数据处理流程
3.2 MapReduce编程规范
MapReduce分布式的运算程序需要分成2个阶段,分别是Map阶段和Reduce阶段。Map阶段对应的是MapTask并发实例,完全并行运行。Reduce阶段对应的是ReduceTask并发实例,数据依赖于上一个阶段所有MapTask并发实例的数据输出结果。
MapReduce编程模型只能包含一个Map阶段和一个Reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个MapReduce程序,串行运行。
用户编写的程序分成三个部分:Mapper,Reducer,Driver(提交运行mr程序的客户端驱动)。
用户自定义的Mapper和Reducer都要继承各自的父类。Mapper中的业务逻辑写在map()方法中,Reducer的业务逻辑写在reduce()方法中。整个程序需要一个Driver来进行提交,提交的是一个描述了各种必要信息的job对象。
最需要注意的是:整个MapReduce程序中,数据都是以kv键值对的形式流转的。因此在实际编程解决各种业务问题中,需要考虑每个阶段的输入输出kv分别是什么。并且在MapReduce中数据会因为某些默认的机制进行排序进行分组。所以说kv的类型数据确定极其重要。
3.3 Map Reduce工作执行流程
整个MapReduce工作流程可以分为3个阶段:map 、 shuffle 、 reduce。
-
map阶段:
- 负责把从数据源读取来到数据进行处理,默认情况下读取数据返回的是kv键值对类型,经过自定义map方法处理之后,输出的也应该是kv键值对类型。
-
shuffle阶段:
- map输出的数据会经过分区、排序、分组等自带动作进行重组,相当于洗牌的逆过程。这是MapReduce的核心所在,也是难点所在。也是值得我们深入探究的所在。
- 默认分区规则:key相同的分在同一个分区,同一个分区被同一个reduce处理。
- 默认排序规则:根据key字典序排序
- 默认分组规则:key相同的分为一组,一组调用reduce处理一次。
-
reduce阶段:
- 负责针对shuffle好的数据进行聚合处理。输出的结果也应该是kv键值对。
4. Hadoop序列化机制
4.1 什么是序列化
序列化 (Serialization)是将结构化对象转换成字节流以便于进行网络传输或写入持久存储的过程。
反序列化(Deserialization)是将字节流转换为一系列结构化对象的过程,重新创建该对象。
序列化的用途:
1、作为一种持久化格式。
2、作为一种通信的数据格式。
3、作为一种数据拷贝、克隆机制。
简单概况:
把对象转换为字节序列的过程称为对象的序列化
把字节序列恢复为对象的过程称为对象的反序列化
4.2 Java的序列化机制
Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端。这就需要有一种可以在两端传输数据的协议。Java序列化机制就是为了解决这个问题而产生。
Java对象序列化的机制,把对象表示成一个二进制的字节数组,里面包含了对象的数据,对象的类型信息,对象内部的数据的类型信息等等。通过保存或则转移这些二进制数组达到持久化、传递的目的。
要实现序列化,需要实现java.io.Serializable接口。反序列化是和序列化相反的过程,就是把二进制数组转化为对象的过程。
4.3 Hadoop的序列化机制
Hadoop的序列化没有采用java的序列化机制,而是实现了自己的序列化机制。
原因在于java的序列化机制比较臃肿,重量级,是不断的创建对象的机制,并且会额外附带很多信息(校验、继承关系系统等)。但在Hadoop的序列化机制中,用户可以复用对象,这样就减少了java对象的分配和回收,提高了应用效率。
Hadoop通过Writable接口实现的序列化机制,不过没有提供比较功能,所以和java中的Comparable接口合并,提供一个接口WritableComparable。(自定义比较)。
Writable接口提供两个方法(write和readFields)。
package org.apache.hadoop.io;
public interface Writable {
void write(DataOutput out) throws IOException;
void readFields(DataInput in) throws IOException;
}
Hadoop序列化特点:高效、紧凑、扩展性强。
4.4 Hadoop中数据类型
Hadoop提供了如下内容的数据类型,这些数据类型都实现了WritableComparable接口,以便用这些类型定义的数据可以被序列化进行网络传输和文件存储,以及进行大小比较。
| Hadoop 数据类型 | Java数据类型 | 备注 |
|---|---|---|
| BooleanWritable | boolean | 标准布尔型数值 |
| ByteWritable | byte | 单字节数值 |
| IntWritable | int | 整型数 |
| FloatWritable | float | 浮点数 |
| LongWritable | long | 长整型数 |
| DoubleWritable | double | 双字节数值 |
| Text | String | 使用UTF8格式存储的文本 |
| MapWritable | map | 映射 |
| ArrayWritable | array | 数组 |
| NullWritable | null | 当<key,value>中的key或value为空时使用 |
注意:如果需要将自定义的类放在key中传输,则还需要实现Comparable接口,因为MapReduce框中的Shuffle过程要求对key必须能排序。