文章目录
1.Hadoop概述
Apache开源软件基金会开发的,运行于大规模普通服务器上的,大数据存储、计算、分析的,分布式存储系统和分布式运算框架
1.0和2.0的差别
1.0块的默认大小是64M,2.0以后是128M
2.0加入了yarn资源管理系统
组成
-
分布式存储系统HDFS(Hadoop Distributed File System) namenode 管理者,在主节点。
存放元数据信息:文件大小、位置、块、索引等 datanode在从节点,存数据的位置,是数据落地的地方 -
资源管理系统YARN
resourcemanager对资源的总的管理
nodemanager在从节点上,把节点的资源信息汇报给resourcemanager -
分布式计算框架MapReduce
MapReduce是一种可用于数据处理的编程模型。该模型比较简单,但用于编写有用的程序并不简单。Hadoop可以运行由各种语言编写的MapReduce程序。例如:Java、Python和C++语言等。最重要的是,MapReduce程序本质上是并行运行的,因此可以将大规模的数据分析任务交给任何一个拥有足够多机器的运行商。MapReduce的优势在于处理大规模数据集
从MapReduce自身的命名特点可以看出,MapReduce由两个阶段组成:Map和Reduce。用户只需编写map()和reduce()两个函数,即可完成简单的分布式程序设计
map()函数 以key/value对作为输出,产生另外一系列key/value对作为中间输出写入本地磁盘。MapReduce框架会自动将这些中间数据按照key值进行聚合,且key值相同的数据被统一交给reduce()函数处理。
reduce() 函数以key及对应的value列表作为输入,经合并key相同的value值后,产生另外一系列key/value对作为最终输出写入HDFS。
2.Hadoop生态圈
-
Hive 构建在Hadoop之上的数据仓库,no sql的类SQL数据库
-
pig
由Yahoo!开源,设计动机是提供一种基于MapReduce的数据分析工具 -
Mahout
基于Hadoop的机器学习和数据挖掘的分布式计算框架 -
Hbase
面向列的数据库 -
Zookeeper
来源于Google的Chubby论文 -
Sqoop
连接Hadoop与传统数据库之间的桥梁 -
Flume
Cloudera开源的日志收集系统 -
Oozie
统一管理和调度这些框架和作业
3.HDFS概述
HDFS(Hadoop Distributed File System)是Hadoop项目的核心子项目,是分布式计算中数据存储管理的基础,是基于流数据模式访问和处理超大文件的需求而开发的,可以运行于廉价的商用服务器上。它所具有的高容错、高可靠性、高可扩展性、高获得性、高吞吐率等特征为海量数据提供了不怕故障的存储,为超大数据集(Large Data Set)的应用处理带来了很多便利。
3.1. 设计思想
- 分而治之(安全性):将大文件、大批量文件,分布式的存放在大量服务器上
- 存储方式:2.0以后,块的默认大小:128M
- 相关特性
高容错、可扩展性及可配置性强
跨平台
Shell命令接口
机架感知功能
负载均衡
web界面
- 设计目标
1.可以检测和快速恢复硬件故障,故障的检测和快速自动恢复是HDFS的一个核心目标
2.流式数据访问
设计成适合进行批量处理,重视数据吞吐量,而不是数据访问的反应速度
3.大规模数据集:支持大文件的存储
4.移动计算代价比移动数据代价低
一个应用请求的计算,离他操作的数据越近就越高效,这在数据达到海量级别的时候更是如此
5.可移植性
HDFS在设计时就考虑到平台的可移植性,这种特性方便了HDFS作为大规模数据应用平台的推广
3.2. 架构解析
3.2.1 namenode
就是master,是一个主管、管理者。管理HDFS的名称空间。主节点仅存放元数据信息(文件大小、位置、块索引)。配置副本策略,处理客户端读写请求,分配client去访问哪个从节点
Namenode对数据的管理
内存元数据metadata
磁盘数据镜像文件fsimage
数据操作日志文件edits
存储机制
内存中有一份完整的元数据(内存metadata)
磁盘有一个”准完整“的元数据镜像文件(在NN的工作目录中)
用于衔接内存metadata和持久化元数据镜像fsimage之间的操作日志(edits文件)
3.2.2 datanode
就是slave,从节点存放真实文件(实际文件)即实际的数据块,执行数据块的读写操作。
namenode下达命令,datanode执行实际的操作。
client客户端发起读取数据的请求,namenode接到请求后会通过元数据信息得到数据位置,最后由datanode将数据最后输出给client客户端
DataNode工作机制
工作职责:存储管理用户的文件块数据
定期向 NameNode 汇报自身所持有的 block 信息(通过心跳信息上报)
3.2.3 SecondaryNamenode
主要是用来保持系统的最新快照,保持系统的更新和响应以及避免数据丢失,类似于WINDOS的快照,防止崩溃。
Secondary NameNode所做的不过是在文件系统中设置一个检查点来帮助NameNode更好的工作。它只是NameNode的一个助手节点。这也是它在社区内被认为是检查点节点的原因。它不是要取代掉NameNode也不是NameNode的备份。所以从现在起,让我们养成一个习惯,称呼它为检查点节点吧。
fsimage 它是在NameNode启动时对整个文件系统的快照
edit logs 它是在NameNode启动后,对文件系统的改动序列
(1) SNN通知NN切换edits文件
(2) SNN从NN获得fsimage和edits
(3) SNN将fsimage载入内存,然后开始合并edits
(4)SNN将新的fsimage发回给NN
(5)NN用新的fsimage替换旧的fsimage
3.3. 架构内容
- 数据块:默认128M,datanode通过数据块存储。存放在hadoopdata目录下
- 心跳机制:主从节点间通过心跳机制确定互相之间的关系每3秒发送一次
- 元数据管理
- 负载均衡:存储数据时,由namenode进行分配存在位置
- 副本存放策略:每个块都有两个副本
- 机架感知:datanode机架感知互相联系
3.4. 优缺点
优点
- (1) 高容错性: 数据自动保存多个副本(默认有三个) 当某一个副本丢失后,它可以自动恢复,这是HDFS的内部机制
- (2) 适合处理超大文件 可以批处理,是通过移动计算而不是移动数据 处理数据GB、TB、甚至PB级数据百万规模以上的文件数量
- (3) 流式文件访问 一次性写入,像流水一样,一点点流出来的多次读取,保证数据一致性
能保留历史文件,不能任意修改hdfs文件,但是可以追加 - (4)可创建在廉价机器上 通过多副本提高可靠性,提供了容错和恢复机制
缺点
- (1) 低延迟数据访问
适合低延迟与高吞吐率,就是在某一时间写入大量的数据,但是在低延时的情况下是不行的,比如毫秒级以内读取数据,是很难做到的 - (2) 小文件存取 占用namenode大量内存:存储大量小文件(这里的小文件是指小于HDFS系统的Block大小的文件的话,它会占用
NameNode大量的内存来存储文件、目录和块信息。这样是不可取的,因为NameNode的内存总是有限的 - (3) 并发写入、文件随机修改 一个文件只能有一个写,不允许多个线程同时写 仅支持append(追加),不支持文件的随即修改
4. 核心设计
- 心跳机制
- 安全模式
- 副本存放策略
- 负载均衡
4.1 心跳机制
(1) master中有NameNode和ResourceManager,slave中有DataNode和NodeManager
Master 启动的时候会启动一个 IPC(Inter-Process Comunication,进程间通信) Server 服务,等待 Slave 的链接;
Slave 启动时,会主动链接 Master 的 IPC Server 服务,并且每隔 3 秒链接一次 Master,这个间隔时间是可以调整的,参数为 dfs.heartbeat.interval,这个每隔一段时间去链接一次的机制,我们形象地称为心跳。 Slave 通过心跳汇报自己的信息给 Master, Master 也通过心跳给 Slave 下达命令
(2) NameNode 通过心跳得知 DataNode 的状态,ResourceManager 通过心跳得知NodeManager 的状态;
如果 Master 长时间都没有收到 Slave 的心跳,就认为该 Slave 挂掉了
(3)死亡时间计算:
NameNode 感知到DataNode掉线死亡的时长计算:
HDFS 默认的超时时间为 10 分钟+30 秒,这里暂且定义超时时间为 timeout。
计算公式为:
timeout = 2 * heartbeat.recheck.interval + 10 * dfs.heartbeat.interval
默认的 heartbeat.recheck.interval 大小为 5 分钟,dfs.heartbeat.interval 默认的大小为 3 秒。
需 要 注 意 的 是 hdfs-site.xml 配 置 文 件 中 的 heartbeat.recheck.interval 的 单位 为 毫 秒,dfs.heartbeat.interval 的单位为秒。
如果 heartbeat.recheck.interval 设置为 5000(毫秒), dfs.heartbeat.interval设置为 3(秒,默认),则总的超时时间为 40 秒。
示例:
<property>
<name>heartbeat.recheck.interval</name>
<value>5000</value>
</property>
<property>
<name>dfs.heartbeat.interval</name>
<value>3</value>
</property>
4.2 安全模式
(1) safemode 是 NameNode 的一种状态(active/standby/safemode 安全模式)
(2) NameNode 进入安全模式的原理:
NameNode 发现集群中的 block 丢失率达到一定比例时(0.1%), NameNode 就会进入安全模式,在安全模式下,不允许客户端进行任何修改文件的操作,包括上传文件、删除文件、重命名等操作,只能查看元数据信息(比如 ls)。这个丢失率是可以手动配置的,默认是在配置文件 hdfs-default.xml 中定义了最小的副本率为dfs.safemode.threshold.pct=0.999f 。
在 HDFS 集群正常冷启动时, NameNode 也会在 safemode 状态下维持相当长的一段时间,此时你不需要去理会,等待它自动退出安全模式即可 。
在刚运行命令start-dfs.sh时,50070页面显示的信息:
(3) 安全模式常用操作命令:
hdfs dfsadmin -safemode leave //强制 NameNode 退出安全模式
hdfs dfsadmin -safemode enter //进入安全模式
hdfs dfsadmin -safemode get //查看安全模式状态
hdfs dfsadmin -safemode wait //等待,一直到安全模式结束
4.3 副本存放策略
(1) 副本的作用:
数据分块存储和副本的存放,是保证可靠性和高性能的关键 。
(2) 副本存储方法:
将每个文件的数据进行分块存储, 每一个数据块又保存有多个副本, 这些数据块副本分布在不同的机器节点上
(3) 副本存放策略
第一个 block 副本存放在 client 所在的 node 中(如果 client 不在集群范围内,则第一个 node是随机选取的,系统会尝试不选择那些太满或者太忙的 node);
第二个副本放置在与第一个 node相同的机架的不同 node 中(近乎随机选择,系统会尝试不选择那些太满或者太忙的 node);
第三个副本和放在与第二个不同的机架上,随机放在不同的 node 中。
其他副本随即存放
(4) 修改副本数(将副本数修改为1):
第一种方式: 修改集群文件 hdfs-site.xml
<property> 【修改项开始】
<name>dfs.replication</name>
<value>1</value>
</property>【修改项结束】
第二种方式: 命令设置hadoop fs -setrep -R 1 /
4.4 负载均衡
(1) 数据不平衡原因
机器与机器之间磁盘利用率不平衡是 HDFS 集群非常容易出现的情况
情况一:在 DataNode 节点出现故障
情况二:在现有的集群上增添新的 DataNode 的时候
(2) HDFS数据自动平衡方法
影响Balancer的几个参数:
-threshold
默认设置:10,参数取值范围:0-100
参数含义:判断集群是否平衡的阈值。理论上,该参数设置的越小,整个集群就越平衡。
dfs.balance.bandwidthPerSec
默认设置:1048576(1M/S)
参数含义:Balancer运行时允许占用的带宽
(3) HDFS数据自动平衡方法
命令:
sbin/start-balancer.sh
启动数据均衡,默认阈值为 10%
sbin/start-balancer.sh –threshold 5
启动数据均衡,阈值 5%
sbin/stop-balancer.sh
停止数据均衡
自动进行均衡非常慢, 一天能移动的数据量在 10G-10T 的级别,很难满足超大集群的需求。
原因: HDFS 集群默认不允许 balance 操作占用很大的网络带宽,这个带宽是可以调整的。
(4) HDFS数据自动平衡方法
调整带宽:
命令: hdfs dfsadmin -setBalanacerBandwidth 10485760
该数值的单位是字节,上面的配置是 10M/s,默认是 1M/s
在hdfs-site.xml 中设置:
dfs.balance.bandwidthPerSec
10485760
5. HDFS READ(读流程)
(1)客户端向NN发出请求,与NN发生交互调用FileSystem对象的open方法,其实获取的是一个DistributedFileSystem的实例
(2)NN把文件的列表信息发送给client,列表信息即文件的位置索引DistributedFileSystem通过RPC(远程过程调用)获得文件的第一批block的locations,同一block按照重复数会返回多个locations,这些locations按照hadoop拓扑结构排序,距离客户端近的排在前面
(3)client根据列表信息去DN上读取block块信息(客户端会找离它比较近的去读,就近原则)
前两步会返回一个FSDataInputStream对象,该对象会被封装成 DFSInputStream对象,DFSInputStream可以方便的管理datanode和namenode数据流。客户端调用read方法,DFSInputStream就会找出离客户端最近的datanode并连接datanode
数据从datanode源源不断的流向客户端
(4)一直读,直到所有块读完
如果第一个block块的数据读完了,就会关闭指向第一个block块的datanode连接,接着读取下一个block块
*如果只有第一行有信息,也会把整个块读完
(5)关闭读操作
如果第一批block都读完了,DFSInputStream就会去namenode拿下一批blocks的location,然后继续读,如果所有的block块都读完,这时就会关闭掉所有的流
比如要读一个文件a.txt,300M,那么它会占用3个块,默认保存3个副本,所以存在三个DN上
6. HDFS WRITE(写流程)
(1) 客户端根据文件的大小,为它分配需要的block数,然后向NN发出写的请求,看看文件是否存在,如果不存在就创建
-客户端通过调用 DistributedFileSystem 的create方法,创建一个新的文件。
-DistributedFileSystem 通过 RPC(远程过程调用)调用 NameNode,去创建一个没有blocks关联的新文件。创建前,NameNode 会做各种校验,比如文件是否存在,客户端有无权限去创建等。如果校验通过,NameNode 就会记录下新文件,否则就会抛出IO异常
(2) NN为文件分配每个块写的位置,然后向client返回文件list,即元数据信息给client
-第一步结束后会返回 FSDataOutputStream 的对象,和读文件的时候相似,FSDataOutputStream 被封装成 DFSOutputStream,DFSOutputStream 可以协调 NameNode和 DataNode。客户端开始写数据到DFSOutputStream,DFSOutputStream会把数据切成一个个小packet,然后排成队列 data queue。
(3) client根据文件list在相应的DN上写,每写完一个DN都向client返回一个ack信息。
DFSOutputStream 还有一个队列叫 ack queue,也是由 packet 组成,等待DataNode的收到响应,当pipeline中的所有DataNode都表示已经收到的时候,这时akc queue才会把对应的packet包移除掉。
-如果有DN坏了,NN会重新分配,client再重新写
(4) 客户端写完最后一块后,client关闭数据流
-客户端完成写数据后,调用close方法关闭写入流。
-DataStreamer 把剩余的包都刷到 pipeline 里,然后等待 ack 信息,收到最后一个 ack 后,通知 DataNode 把文件标示为已完成。
(5) 当第一块block完成时,它所在的那个节点的后台会启动一个进程进行复制,分别把副本写到其它DN上(管道复制)
-DataStreamer 会去处理接受 data queue,它先问询 NameNode 这个新的 block 最适合存储的在哪几个DataNode里,比如重复数是3,那么就找到3个最适合的 DataNode,把它们排成一个 pipeline。DataStreamer 把 packet 按队列输出到管道的第一个 DataNode 中,第一个 DataNode又把 packet 输出到第二个 DataNode 中,以此类推。
比如要写一个文件a.txt,300M,那么它会占用3个块,默认保存3个副本
7.HDFS操作命令
7.1 HDFS的Shell操作
常用命令
hdfs的ftp的上传目录
[tyhawk@master ~]$ hadoop fs -ls hdfs://master:9000/
hadoop fs -ls /
hadoop fs -lsr / //把每一层的目录都显示出来,类似于Linux的tree命令
hadoop fs -ls -R /tmp
上传(两个命令能达到相同的效果)
[tyhak@master ~]$ hadoop fs -copyFromLocal hadoop-2.7.7.tar.gz /0315
[tyhawk@master hadoop-2.7.7]$ hadoop fs -put README.txt /0315
下载(两个命令能达到相同的效果)
[tyhawk@master hadoop-2.7.7]$ hadoop fs -copyToLocal /0315/README.txt /home/tyhawk/
[tyhawk@master hadoop-2.7.7]$ hadoop fs -get
查看
[tyhwak@master ~]$ hadoop fs -cat /0315/README.txt
创建/删除目录
三种方式
hadoop fs -rm
hadoop fs -rmr
hadoop fs -mkdir /test
7.2 通过JAVA API操作
hdfs 在生产应用中主要是客户端的开发,其核心步骤是从hdfs提供的api中构造一个HDFS的访问客户端对象,然后通过客户端对象操作(增加删除修改)HDFS上传的
(1) 利用eclipse查看hdfs集群的文件信息
下载一个eclipse开发工具
解压安装eclipse
(2)获取客户端实例
- 获取配置信息
(3)从HDFS提供的API中创建一个HDFS的对象,通过这个对象来操作文件系统
(4)检测,打印出访问的uri地址
都要选org.apache.hadoop相关
(5) 编写脚本
- 通过配置来获取fs对象
通过给定两个参数,name是fs.defaultFS,现在访问的就不再是本地,而是hdfs文件系统
如果我们没有给conf设置文件系统,那么FileSystem默认获取的是本地文件系统的一个实例~
若是我们设置了“fs.defaultFS”参数,这表示获取的是该URI的文件系统的实例,就是我们需要的HDFS集群的一个fs对象
conf.set(“fs.defaultFS”, “hdfs://master:9000”); - 直接获取FileSystem对象
不需要配置“fs.defaultFS”参数,直接传入URI和用户身份,最后一个参数是安装hadoop集群的用户,这里的是“yao”
FileSystem fs=FileSystem.get(new URI(“hdfs://master:9000”),conf,“yao”);
- 创建实例对象
- 增删改查
1.创建目录
2.上传文件
默认不删除本地源文件,不覆盖HDFS同名文件
3.下载文件
4.重命名文件或者文件夹
5.删除文件或者文件夹
*说明这是一个过时的命令,但是也可以执行
6.查看
1)查看/目录下的目录
2)查看/test目录下的目录和文件
7.关闭
fs相当于一个资源,如果不关闭,它就一直在运行,是浪费资源
8.YARN平台
8.1产生背景
1.0版本体现出的问题如下
扩展性差:难以支持MR之外的计算
资源利用率低:不能将硬件资源利用到最高
可靠性差:NameNode单点故障
8.2架构与组件
ResourceManager
ResourceManager 是基于应用程序对集群资源的需求进行调度的 Yarn 集群主控节点,负责协调和管理整个集群(所有 NodeManager)的资源,响应用户提交的不同类型应用程序的解析,调度,监控等工作。ResourceManager 会为每一个 Application 启动一个 ApplicationMaster, 并且 ApplicationMaster 分散在各个 NodeManager 节点
NodeManager
NodeManager 是 YARN 集群当中真正资源的提供者,是真正执行应用程序的容器的提供者, 监控应用程序的资源使用情况,并通过心跳向集群资源调度器 ResourceManager 进行汇报。
ApplicationMaster
ApplicationMaster 对应一个应用程序,职责是向资源调度器申请执行任务的资源容器,监控整个任务的执行,跟踪整个任务的状态,处理任务失败以异常情况
Container
Container 是一个抽象出来的逻辑资源单位。它封装了一个节点上的 CPU,内存,磁盘,网络等信息,MapReduce 程序的所有 task 都是在这个容器里执行完成的
8.3作业运行过程
8.3.1. 作业提交
Client端发出请求运行一个Job,请求资源管理器(ResourceManager),获取一个作业ID(应用ID),作业的客户端核实作业的输出, 计算输入的split, 将作业的资源(包括Jar包, 配置文件, split信息)拷贝给HDFS(第3步),最后, 通过调用资源管理器的submitApplication()来提交作业(第4步)
8.3.2. 作业初始化
当资源管理器收到submitApplciation()的请求时, 就将该请求发给调度器(scheduler), 调度器分配第一个container, 然后资源管理器在该container内启动应用管理器(Application Master)进程, 由节点管理器(NodeManager)监控(第5a和5b步)
MapReduce作业的application master是一个Java应用程序,它的主类是MRAppMaster。它对作业进行初始化:通过创建多个对象以保持对作业进度的跟踪,因为他将接受来自任务的进度和完成报告(第6步)
MRAppMaster接受来自共享文件系统的在客户端计算的输入分片(第7步)。对每一个分片创建map任务对象以及educe任务对象。
8.3.3. 任务分配
如果不是小作业, 那么应用管理器向资源管理器请求container来运行所有的map和reduce任务(第8步),每个任务对应一个container,且只能在该container上运行,这些请求是通过心跳来传输的
8.3.4. 任务执行
当一个任务由资源管理器的调度器分配给一个container后, 应用管理器通过练习节点(NodeManager)管理器来启动container(第9a步和9b步). 任务有一个主类为YarnChild的Java应用执行. 在运行任务之前首先本地化任务需要的资源, 比如作业配置, JAR文件, 以及分布式缓存的所有文件(第10步). 最后, 运行map或reduce任务(第11步).
8.3.5. 进度和状态更新
YARN中的任务将其进度和状态(包括counter)返回给应用管理器, 后者通过每3秒的脐带接口有整个作业的视图
8.3.6. 作业完成
除了向应用管理器请求作业进度外, 客户端每5分钟都会通过调用waitForCompletion()来检查作业是否完成
9.MapReduce编程模型
9.1设计目的
- 易于编程
- 良好的扩展性
- 高容错性
9.2WordCount编程实例
9.2.1. 流程
9.2.2. 实例
(1) Hadoop原生实现“单词统计”实现类
(2)首先将Hadoop2.0的安装目录下的README.txt文件上传到HDFS上
(3)然后运行命令
(4)运行结果
(5) 查看源码
解压hadoop2.7.1的源码包
[lan@master ~]$ tar -xvf hadoop-2.7.1-src.tar.gz
进入如下目录查看源码
9.3. WordCount实例编写
9.3.1. 编写Mapper函数类
public class WordMap extends Mapper<Object, Text, Text, IntWritable> {
protected void map(Object key, Text value, Context context)
throws IOException, InterruptedException {
String[] lines = value.toString().split(" ");
for (String word : lines) {
// 每个单词出现1次,作为中间结果输出
context.write(new Text(word), new IntWritable(1));
}
};
}
9.3.2. 编写Reducer函数类
public class WordReduce extends Reducer<Text, IntWritable, Text, IntWritable> {
protected void reduce(Text key, Iterable<IntWritable> values,
Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable count : values) {
sum = sum + count.get();
}
context.write(key, new IntWritable(sum));// 输出最终结果
};
}
context.write(key, new IntWritable(sum));// 输出最终结果
9.3.3. 编写Main函数类
public class WordMain {
public static void main(String[] args) throws IOException,
ClassNotFoundException, InterruptedException {
if (args.length != 2 || args == null) {
System.out.println("please input Path!");
System.exit(0);
}
Configuration configuration = new Configuration();
Job job = new Job(configuration, WordMain.class.getSimpleName());
// 打jar包
job.setJarByClass(WordMain.class);
// 设置输入/输出路径
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 设置处理Map/Reduce阶段的类
job.setMapperClass(WordMap.class);
job.setReducerClass(WordReduce.class);
// 设置最终输出key/value的类型m
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// 提交作业
job.waitForCompletion(true);
}
}
9.4. shuffle 清洗阶段
MapReduce 中,map 阶段处理的数据如何传递给 reduce 阶段,是 MapReduce 框架中最 关键的一个流程,这个流程就叫 Shuffle
shuffle就是洗牌的过程,它会将数据进行分组排序
具体来说它就是将 maptask 输出的处理结果数据,分发给 reducetask,并在分发的过程中,对数据按 key 进行了分组和排序
9.4.1.map端
map函数开始产生输出时,利用缓冲的方式写到内存,并出于效率的考虑进行预排序
每个map任务都有一个环形内存缓冲区,用于存储任务的输出,默认情况是100MB,一旦缓冲内容达到阈值(默认是80%),一个后台线程开始把内容写到磁盘中。在写磁盘过程中,map输出继续被写到缓冲区,但如果在此期间缓冲区被填满,map会阻塞直到写磁盘过程完成。
在写磁盘之前,线程首先根据数据最终要传送到reducer把数据划分成相应的分区,在每个分区中,后台线程按键进行内排序,如果有一个combiner,它会在排序后的输出上运行
一旦内存缓冲区达到溢出写的阈值,就会新建一个溢出写文件,因此在map任务写完其最后一个输出记录之后,会有几个溢出写文件。在任务完成之前,溢出写文件被合并成一个已分区且已排序的输出文件
9.4.2.reduce端
reducer通过HTTP方式得到输出文件的分区,这就是reduce任务的复制阶段(copy phase)。reduce任务有少量复制线程,因此能够并行取得map输出。默认值是5个线程
与map端一样,reduce端将数据复制过来的时候先被复制到内存中,当内存中的数据量到达一定阈值,就启动内存到磁盘的merge,也就是溢写文件,然后会在磁盘中生成了众多的溢写文件,直到没有map端的数据输出为止,然后进行大合并,将多个溢写文件合并成一个文件,最后,直接把数据输入reduce函数,然后输出。