大数据计算的核心思路是移动计算比移动数据更划算
MapReduce
既是一个编程模型,又是一个计算框架
开发人员必须基于 MapReduce 编程模型进行编程开发,然后将程序通过 MapReduce 计算框架分发到 Hadoop 集群中运行
MapReduce
可进行关系代数运算(SQL
计算),矩阵运算(图计算)等。
一、编程模型
编程模型只包含 Map
和 Reduce
两个过程
Map
的主要输入是一对<key, value>
值,经过Map
计算后输出一对<key, value>
值;然后将相同key
合并,形成List<key, value>
- 再将这个
List<key, value>
输入Reduce
,经过计算输出零个或多个<key, value>
对
以 WordCount
举个栗子
WordCount
计算过程
需求:统计文本中词频(每个单词出现的次数)
这个文本很大,大到无法全部加载进内存
首先,文本被切割并存储,例如切割成块并存储在 HDFS
计算过程如图:
二、计算框架
问题:
- 上个
WordCount
程序如何在分布式集群中运行起来?MapReduce
程序又如何找到相应的数据并进行计算?
这两个问题可扩展为:
-
如何为每个数据块分配一个
Map
计算任务,也就是代码是如何发送到数据块所在服务器的,发送后是如何启动的,启动以后如何知道自己需要计算的数据在文件什么位置(BlockID
是什么)。 -
处于不同服务器的
Map
输出的 ,如何把相同的Key
聚合在一起发送给Reduce
任务进行处理。
这两个问题分别如下图红圈:
(1)MR
作业启动
主要包括:Map
过程 和 Reduce
过程
MapReduce
运行过程涉及三类关键进程:
- 大数据应用进程。
这类进程是启动
MapReduce
程序的主入口,主要是指定Map
和Reduce
类、输入输出文件路径等,并提交作业给Hadoop
集群,JobTracker
进程。这是由用户启动的MapReduce
程序进程,比如WordCount
程序。
JobTracker
进程。
这类进程根据要处理的输入数据量,命令下面提到的
TaskTracker
进程启动相应数量的Map
和Reduce
进程任务,并管理整个作业生命周期的任务调度和监控。这是 Hadoop 集群的常驻进程,需要注意的是,JobTracker
进程在整个Hadoop
集群全局唯一。
TaskTracker
进程。
这个进程负责启动和管理
Map
进程以及Reduce
进程。因为需要每个数据块都有对应的map
函数,TaskTracker
进程通常和HDFS
的DataNode
进程启动在同一个服务器。也就是说,Hadoop
集群中绝大多数服务器同时运行DataNode
进程和TaskTracker
进程。
整理流程入下图:
-
应用进程
JobClient
将用户作业JAR
包存储在HDFS
中,将来这些JAR
包会分发给Hadoop
集群中的服务器执行MapReduce
计算。 -
应用程序提交
job
作业给JobTracker
。
3.JobTracker
根据作业调度策略创建 JobInProcess
树,每个作业都会有一个自己的 JobInProcess
树。
4.JobInProcess
根据输入数据分片数目(通常情况就是数据块的数目)和设置的 Reduce
数目创建相应数量的 TaskInProcess
。
5.TaskTracker
进程和 JobTracker
进程进行定时通信。
- 如果
TaskTracker
有空闲的计算资源(有空闲 CPU 核心),JobTracker
就会给它分配任务。分配任务的时候会根据TaskTracker
的服务器名字匹配在同一台机器上的数据块计算任务给它,使启动的计算任务正好处理本机上的数据,以实现“移动计算比移动数据更划算”。
7.TaskTracker
收到任务后根据任务类型(是 Map
还是 Reduce
)和任务参数(作业 JAR
包路径、输入数据文件路径、要处理的数据在文件中的起始位置和偏移量、数据块多个备份的 DataNode
主机名等),启动相应的 Map
或者 Reduce
进程。
8.Map
或者 Reduce
进程启动后,检查本地是否有要执行任务的 JAR
包文件,如果没有,就去 HDFS
上下载,然后加载 Map
或者 Reduce
代码开始执行。
- 如果是
Map
进程,从HDFS
读取数据(通常要读取的数据块正好存储在本机);如果是Reduce
进程,将结果数据写出到HDFS
。
(2)MR
数据合并与连接机制
主要是:shuffle
过程
shuffle
:在 map
输出与 reduce
输入之间,MapReduce
计算框架处理数据合并与连接操作。
分布式计算需要将不同服务器上的相关数据合并到一起进行下一步计算
如下图:
每个 Map
任务的计算结果都会写入到本地文件系统,等 Map
任务快要计算完成的时候,MapReduce
计算框架会启动 shuffle
过程,在 Map
任务进程调用一个 Partitioner
接口,对 Map
产生的每个 <key, value>
进行 Reduce
分区选择,然后通过 HTTP
通信发送给对应的 Reduce
进程。这样不管 Map
位于哪个服务器节点,相同的 Key
一定会被发送给相同的 Reduce
进程。Reduce
任务进程对收到的 进行排序和合并,相同的 Key
放在一起,组成一个 传递给 Reduce
执行。
map
输出的 shuffle
到哪个 Reduce
进程是这里的关键,它是由 Partitioner
来实现,MapReduce
框架默认的 Partitioner
用 Key
的哈希值对 Reduce
任务数量取模,相同的 Key
一定会落在相同的 Reduce
任务 ID 上。从实现上来看的话,这样的 Partitioner
代码只需要一行。
public int getPartition(K2 key, V2 value, int numReduceTasks) {
return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}