注:本文主要摘录于尚硅谷大数据的学习资料,仅作学习记录,请勿用于商业用途。
MapReduce
Hadoop 序列化
1
为什么要序列化?
一般来说,“活的”对象只生存在内存里,关机断电就没有了。而且“活的”对象只能
由本地的进程使用,不能被发送到网络上的另外一台计算机。 然而序列化可以存储“活的”
对象,可以将“活的”对象发送到远程计算机。
2
什么是序列化?
序列化就是把内存中的对象,转换成字节序列(或其他数据传输协议)以便于存储(持
久化)和网络传输。
反序列化就是将收到字节序列(或其他数据传输协议)或者是硬盘的持久化数据,转换
成内存中的对象。
3
为什么不用
Java
的序列化?
Java
的序列化是一个重量级序列化框架(
Serializable
),一个对象被序列化后,会附带
很多额外的信息(各种校验信息,
header
,继承体系等),不便于在网络中高效传输。所以,
hadoop
自己开发了一套序列化机制(
Writable
),精简、高效。
4
为什么序列化对
Hadoop
很重要?
因为
Hadoop
在集群之间进行通讯或者
RPC
调用的时候,需要序列化,而且要求序列
化要快,且体积要小,占用带宽要小。所以必须理解
Hadoop
的序列化机制。
序列化和反序列化在分布式数据处理领域经常出现:进程通信和永久存储。然而
Hadoop
中各个节点的通信是通过远程调用(
RPC
)实现的,那么
RPC
序列化要求具有以下特点:
1
)紧凑:紧凑的格式能让我们充分利用网络带宽,而带宽是数据中心最稀缺的资
2
)快速:进程通信形成了分布式系统的骨架,所以需要尽量减少序列化和反序列化的
性能开销,这是基本的;
3
)可扩展:协议为了满足新的需求变化,所以控制客户端和服务器过程中,需要直接
引进相应的协议,这些是新协议,原序列化方式能支持新的协议报文;
4
)互操作:能支持不同语言写的客户端和服务端进行交互;
MapReduce 工作流程
2
)流程详解
上面的流程是整个
mapreduce
最全工作流程,但是
shuffle
过程只是从第
7
步开始到第
16
步结束,具体
shuffle
过程详解,如下:
1
)
maptask
收集我们的
map()
方法输出的
kv
对,放到内存缓冲区中
2
)从内存缓冲区不断溢出本地磁盘文件,可能会溢出多个文件
3
)多个溢出文件会被合并成大的溢出文件
4
)在溢出过程中,及合并的过程中,都要调用
partitioner
进行分区和针对
key
进行排
序
5
)
reducetask
根据自己的分区号,去各个
maptask
机器上取相应的结果分区数据
6
)
reducetask
会取到同一个分区的来自不同
maptask
的结果文件,
reducetask
会将这些
文件再进行合并(归并排序)
7
)合并成大文件后,
shuffle
的过程也就结束了,后面进入
reducetask
的逻辑运算过程
(从文件中取出一个一个的键值对
group
,调用用户自定义的
reduce()
方法)
3
)注意
Shuffle
中的缓冲区大小会影响到
mapreduce
程序的执行效率,原则上说,缓冲区越大,
磁盘
io
的次数越少,执行速度就越快。
缓冲区的大小可以通过参数调整,参数:
io.sort.mb
默认
100M
。
MapTask 工作机制
1
并行度决定机制
MapTask
并行度决定机制
一个
job
的
map
阶段
MapTask
并行度(个数),由客户端提交
job
时的切片个数决定。
MapTask 工作机制
(
1
)
Read
阶段:
Map Task
通过用户编写的
RecordReader
,从输入
InputSplit
中解析出
一个个
key/value
。
(
2
)
Map
阶段:该节点主要是将解析出的
key/value
交给用户编写
map()
函数处理,并
产生一系列新的
key/value
。
(
3
)
Collect
收集阶段:在用户编写
map()
函数中,当数据处理完成后,一般会调用
OutputCollector.collect()
输出结果。在该函数内部,它会将生成的
key/value
分区(调用
Partitioner
),并写入一个环形内存缓冲区中。
(
4
)
Spill
阶段:即
“
溢写
”
,当环形缓冲区满后,
MapReduce
会将数据写到本地磁盘上,
生成一个临时文件。需要注意的是,将数据写入本地磁盘之前,先要对数据进行一次本地排
序,并在必要时对数据进行合并、压缩等操作。
溢写阶段详情:
步骤
1
:利用快速排序算法对缓存区内的数据进行排序,排序方式是,先按照分区编号
partition
进行排序,然后按照
key
进行排序。这样,经过排序后,数据以分区为单位聚集在
一起,且同一分区内所有数据按照
key
有序。
步骤
2
:按照分区编号由小到大依次将每个分区中的数据写入任务工作目录下的临时文
件
output/spillN.out
(
N
表示当前溢写次数)中。如果用户设置了
Combiner
,则写入文件之
前,对每个分区中的数据进行一次聚集操作。
步骤
3
:将分区数据的元信息写到内存索引数据结构
SpillRecord
中,其中每个分区的元
信息包括在临时文件中的偏移量、压缩前数据大小和压缩后数据大小。如果当前内存索引大
小超过
1MB
,则将内存索引写到文件
output/spillN.out.index
中。
(
5
)
Combine
阶段:当所有数据处理完成后,
MapTask
对所有临时文件进行一次合并,
以确保最终只会生成一个数据文件。
当所有数据处理完后,
MapTask
会将所有临时文件合并成一个大文件,并保存到文件
output/file.out
中,同时生成相应的索引文件
output/file.out.index
。
在进行文件合并过程中,
MapTask
以分区为单位进行合并。对于某个分区,它将采用多
轮递归合并的方式。每轮合并
io.sort.factor
(默认
100
)个文件,并将产生的文件重新加入
待合并列表中,对文件排序后,重复以上过程,直到最终得到一个大文件。
让每个
MapTask
最终只生成一个数据文件,可避免同时打开大量文件和同时读取大量
小文件产生的随机读取带来的开销。
Shuffle 机制
WritableComparable
排序
排序是
MapReduce
框架中最重要的操作之一。
Map Task
和
Reduce Task
均会对数据(按
照
key
)进行排序。该操作属于
Hadoop
的默认行为。任何应用程序中的数据均会被排序,
而不管逻辑上是否需要。
默认排序是按照字典顺序排序,且实现该排序的方法是快速排序。
对于
Map Task
,它会将处理的结果暂时放到一个缓冲区中,当缓冲区使用率达到一定
阈值后,再对缓冲区中的数据进行一次排序,并将这些有序数据写到磁盘上,而当数据处理
完毕后,它会对磁盘上所有文件进行一次合并,以将这些文件合并成一个大的有序文件。
对于
Reduce Task
,它从每个
Map Task
上远程拷贝相应的数据文件,如果文件大小超过
一定阈值,则放到磁盘上,否则放到内存中。如果磁盘上文件数目达到一定阈值,则进行一
次合并以生成一个更大文件;如果内存中文件大小或者数目超过一定阈值,则进行一次合并
后将数据写到磁盘上。当所有数据拷贝完毕后,
Reduce Task
统一对内存和磁盘上的所有数
据进行一次合并。
每个阶段的默认排序
1
)排序的分类:
(
1
)部分排序:
MapReduce
根据输入记录的键对数据集排序。保证输出的每个文件内部排序。
(
2
)全排序:
如何用
Hadoop
产生一个全局排序的文件?最简单的方法是使用一个分区。但该方法在
处理大型文件时效率极低,因为一台机器必须处理所有输出文件,从而完全丧失了
MapReduce
所提供的并行架构。
替代方案:首先创建一系列排好序的文件;其次,串联这些文件;最后,生成一个全局
排序的文件。主要思路是使用一个分区来描述输出的全局排序。例如:可以为上述文件创建
3
个分区,在第一分区中,记录的单词首字母
a-g
,
第二分区记录单词首字母
h-n,
第三分区
记录单词首字母
o-z
。
(
3
)辅助排序:(
GroupingComparator
分组)
Mapreduce
框架在记录到达
reducer
之前按键对记录排序,但键所对应的值并没有被排
序。甚至在不同的执行轮次中,这些值的排序也不固定,因为它们来自不同的
map
任务且
这些
map
任务在不同轮次中完成时间各不相同。一般来说,大多数
MapReduce
程序会避免
让
reduce
函数依赖于值的排序。但是,有时也需要通过特定的方法对键进行排序和分组等
以实现对值的排序。
(
4
)二次排序:
在自定义排序过程中,如果
compareTo
中的判断条件为两个即为二次排序。
Combiner
合并
1
)
combiner
是
MR
程序中
Mapper
和
Reducer
之外的一种组件。
2
)
combiner
组件的父类就是
Reducer
。
3
)
combiner
和
reducer
的区别在于运行的位置:
Combiner
是在每一个
maptask
所在的节点运行
;
Reducer
是接收全局所有
Mapper
的输出结果;
4
)
combiner
的意义就是对每一个
maptask
的输出进行局部汇总,以减小网络传输量。
5
)
combiner
能够应用的前提是不能影响最终的业务逻辑,而且,
combiner
的输出
kv
应该
跟
reducer
的输入
kv
类型要对应起来。
ReduceTask 工作机制
(
1
)
Copy
阶段:
ReduceTask
从各个
MapTask
上远程拷贝一片数据,并针对某一片数
据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。
(
2
)
Merge
阶段:在远程拷贝数据的同时,
ReduceTask
启动了两个后台线程对内存和
磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。
(
3
)
Sort
阶段:按照
MapReduce
语义,用户编写
reduce()
函数输入数据是按
key
进行
聚集的一组数据。为了将
key
相同的数据聚在一起,
Hadoop
采用了基于排序的策略。由于
各个
MapTask
已经实现对自己的处理结果进行了局部排序,因此,
ReduceTask
只需对所有
数据进行一次归并排序即可。
(
4
)
Reduce
阶段:
reduce()
函数将计算结果写到
HDFS
上。
Yarn 概述
Yarn
是一个资源调度平台,负责为运算程序提供服务器运算资源,相当于一个分布式
的
操作系统平台
,而
MapReduce
等运算程序则相当于运行于
操作系统之上的应用程序
。
Yarn
基本架构
YARN
主要由
ResourceManager
、
NodeManager
、
ApplicationMaster
和
Container
等组件 构成。
工作机制详解
(
0
)
Mr
程序提交到客户端所在的节点。
(
1
)
Yarnrunner
向
Resourcemanager
申请一个
Application
。
(
2
)
rm
将该应用程序的资源路径返回给
yarnrunner
。
(
3
)该程序将运行所需资源提交到
HDFS
上。
(
4
)程序资源提交完毕后,申请运行
mrAppMaster
。
(
5
)
RM
将用户的请求初始化成一个
task
。
(
6
)其中一个
NodeManager
领取到
task
任务。
(
7
)该
NodeManager
创建容器
Container
,并产生
MRAppmaster
。
(
8
)
Container
从
HDFS
上拷贝资源到本地。
(
9
)
MRAppmaster
向
RM
申请运行
maptask
资源。
(
10
)
RM
将运行
maptask
任务分配给另外两个
NodeManager
,另两个
NodeManager
分
别领取任务并创建容器。
(
11
)
MR
向两个接收到任务的
NodeManager
发送程序启动脚本,这两个
NodeManager
分别启动
maptask
,
maptask
对数据分区排序。
(
12
)
MrAppMaster
等待所有
maptask
运行完毕后,向
RM
申请容器,运行
reduce task
。
(
13
)
reduce task
向
maptask
获取相应分区的数据。
(
14
)程序运行完毕后,
MR
会向
RM
申请注销自己。
作业提交全过程详解
(
1
)作业提交
第
0
步:
client
调用
job.waitForCompletion
方法,向整个集群提交
MapReduce
作业。
第
1
步:
client
向
RM
申请一个作业
id
。
第
2
步:
RM
给
client
返回该
job
资源的提交路径和作业
id
。
第
3
步:
client
提交
jar
包、切片信息和配置文件到指定的资源提交路径。
第
4
步:
client
提交完资源后,向
RM
申请运行
MrAppMaster
。
(
2
)作业初始化
第
5
步:当
RM
收到
client
的请求后,将该
job
添加到容量调度器中。
第
6
步:某一个空闲的
NM
领取到该
job
。
第
7
步:该
NM
创建
Container
,并产生
MRAppmaster
。
第
8
步:下载
client
提交的资源到本地。
(
3
)任务分配
第
9
步:
MrAppMaster
向
RM
申请运行多个
maptask
任务资源。
第
10
步:
RM
将运行
maptask
任务分配给另外两个
NodeManager
,另两个
NodeManager
分别领取任务并创建容器。
(
4
)任务运行
第
11
步:
MR
向两个接收到任务的
NodeManager
发送程序启动脚本,这两个
NodeManager
分别启动
maptask
,
maptask
对数据分区排序。
第
12
步:
MrAppMaster
等待所有
maptask
运行完毕后,向
RM
申请容器,运行
reduce task
。
第
13
步:
reduce task
向
maptask
获取相应分区的数据。
第
14
步:程序运行完毕后,
MR
会向
RM
申请注销自己。
(
5
)进度和状态更新
YARN
中的任务将其进度和状态
(
包括
counter)
返回给应用管理器
,
客户端每秒
(
通过
mapreduce.client.progressmonitor.pollinterval
设置
)
向应用管理器请求进度更新
,
展示给用户。
(
6
)作业完成
除了向应用管理器请求作业进度外
,
客户端每
5
分钟都会通过调用
waitForCompletion()
来检查作业是否完成。时间间隔可以通过
mapreduce.client.completion.pollinterval
来设置。作
业完成之后
,
应用管理器和
container
会清理工作状态。作业的信息会被作业历史服务器存储
以备之后用户核查。
作业提交过程之 MapReduce