一、环形缓冲区
1.缓冲区概念
在MapTask执行自定义的计算的过程中需要执行context.write进行 输出 ,他不是直接就把结果存在了硬盘上,而是会先存在内存中 ,当都达到了 一定的条件后才会向硬盘中写文件。存在内存中的这个 区域就叫做缓冲区
缓冲区是一块内存空间,默认大小 是100M,对自定义Mapper中context.write输出进行临时存储,mapper的输出会先被计算分区编号,然后连同mapper输出的key-value一同放入环形缓冲区,当环形缓冲区存储比率达到80%的时候,会触发溢写操作。这两个 默认值都可以通过修改配置文件进行更改。
2.组件详解
1. 环形缓冲区
他的底层是使用数组实现的。每个MapTask的输出结果会根据相应的分区不停地输出到在内存中构建的一个环形结构中。使用环形结构可以更好的利用储存空间存更多的数据。这个数据结构其实就是个字节数组byte[],叫Kvbuffer,名如其义,但是这里面不光放置了数据,还放置了一些索引数据,给放置索引数据的区域起了一个Kvmeta的别名,在Kvbuffer的一块区域上起了一个IntBuffer(字节序采用的是平台自身的字节序)的名字。数据区域和索引数据区域在Kvbuffer中是相邻不重叠的两个区域,用一个分界点来划分两者,分界点不是不变的,而是每次Spill之后都会更新一次。初始的分界点是0,数据的存储方向是向上增长,索引数据的存储方向是向下增长.
二、Spill溢写
由于内存的容量是有限的,当MapTask的context.write还没有执行完的时候,缓冲区中存储 的键值对和分区信息等这些东西可能都将要占满内存空间了,这个时候会自动对缓冲区进行刷新,将已经计算好的数据,按照key排序先输出一个临时的文件。将缓冲区的空间腾出来供后续使用。
溢写也是一个单独的线程
溢写就是将缓冲区的数据刷新的磁盘的过程, 溢写的过程:根据分区号的不同 ,再根据key进行排序, MapTask有可能还没有处理完所有的数据,就触发了溢写操作,就有可能触发多次溢写。当最后一次执行Mapper的map方法之后,也会触发溢写
三、Shuffle
1. 概念:
站在数据角度,k-v从Mapper离开,一直到传给Reducer方法,中间过程,叫做shuffle
2.过程
Mapper阶段
2. 循环调用mapper.map(k,v)
关键代码:
while(xx.next){
mapper.map(k,v);
}
3. mapper.map执行完毕后,输出k-v,调用k-v的分区计算
Partitioner.getPartition(k,v,reduceTask数量)--分区号。
4. 将输出k-v{分区号},存入临时缓冲区。环形缓冲区。
MapOutputBuffer--环形缓冲区。
5. 如果缓冲区写满80%(mapper代码执行完毕),触发spill溢写过程。
① 读取k-v{分区号},对溢写范围内的数据进行排序。
② 存放到本地磁盘文件中,产生分区内的溢写文件。
6. 溢写完毕后,产生多个溢写文件
① 将多个溢写文件合并成1个有序---归并排序。
② combiner(分区 合并 调用reducer--局部reduce操作)【如果开启】
Reducer阶段
1. 从各个MapTask节点下载对应分区的结果文件。
MapTask(分区0文件)
MapTask(分区0文件)→ ReduceTask-0
MapTask(分区0文件)
2. merge操作
① 排序
② 按照key分组
③ 将key相同的多个value--->[v,v,v,v]
3. 循环调用Reducer.reduce方法处理数据
while(xxx){
reducer.reduce(k,vs);
}
map阶段
1. mapper输出结果
① 获得ko-vo获得分区号。(Partitioner.getpartion())
② 将ko-vo写出到环形缓冲区中。
2. 一旦环形缓冲区中数据达到溢写条件(80%,写完了)
① 读取环形缓冲区中的数据
② 根据分区号,分区排序、(Combiner)
③ 将处理结果溢写到磁盘中文件中。
④ 每次达到溢写条件(80%,写完了),①~③,在mapTask本地磁盘形成分区文件。
⑤ 最后在本地完成一次分区内多个溢写文件 归并排序离开健康,产生1个文件(maptask处理结果)。
reduce阶段
3. 根据分区号,启动ReduceTask,下载多个MapTask处理结果中的对应分区文件
MapTaskA(分区0)----ReduceTask0
MapTaskB(分区0)----ReduceTask0
4. 将当前分区中,来自不同MapTask的分区文件,归并排序。(为了reduce的merge操作效率)
产生1个大的本分区的文件,且内容key有序。
5. merge操作,将有序的结果,合并key的value。
6. 逐步读取k-vs,调用reducer.reduce();
四、MapReduce总结
mapreduce工作流程
任务提交
1. 拆-split逻辑切片--任务切分。
FileInputFormat--split切片计算工具
FileSplit--单个计算任务的数据范围。
2. 获得split信息和个数。
MapTask阶段
1. 读取split范围内的数据。k(偏移量)-v(行数据)
关键API:TextInputFormat。
2. 循环调用mapper.map(k,v)
关键代码:
while(xx.next){
mapper.map(k,v);
}
3. mapper.map执行完毕后,输出k-v,调用k-v的分区计算
Partitioner.getPartition(k,v,reduceTask数量)--分区号。
4. 将输出k-v{分区号},存入临时缓冲区。环形缓冲区。
MapOutputBuffer--环形缓冲区。
5. 如果缓冲区写满80%(mapper代码执行完毕),触发spill溢写过程。
① 读取k-v{分区号},对溢写范围内的数据进行排序。
② 存放到本地磁盘文件中,产生分区内的溢写文件。
6. 溢写完毕后,产生多个溢写文件
① 将多个溢写文件合并成1个有序---归并排序。
② combiner(分区 合并 调用reducer--局部reduce操作)【如果开启】
结果:
每个MapTask执行完毕后本地磁盘,每个分区(目录)内只有一个文件。(Key有序)
ReduceTask阶段
1. 从各个MapTask节点下载对应分区的结果文件。
MapTask(分区0文件)
MapTask(分区0文件)→ ReduceTask-0
MapTask(分区0文件)
2. merge操作
① 排序
② 按照key分组
③ 将key相同的多个value--->[v,v,v,v]
3. 循环调用Reducer.reduce方法处理数据
while(xxx){
reducer.reduce(k,vs);
}
4. reducer.reduce输出key-value,将数据写入HDFS中。
TextOutputForamt 格式化数据的工具类
FileOutputFormat 指定输出HDFS的路径位置。
整个过程排序的次数:3次:
第一次: MapTask阶段环形缓冲区开始spill溢写,缓冲区每次溢写,发生一轮排序。
第二次: Maptask多次溢写产生的多个溢写文件(单个文件每部k有序),要做归并排序,maptask每个分区内,只保留1个文件(key有序)
第三次: ReduceTask-0汇总多个MapTask的(对应分区-0)结果文件,归并排序(合并排序)
五、Yarn分布式环境部署
1.搭建步骤
- 前提是在已经部署了HDFS的三台CentOS7 上搭建Yarn
- 只用修改两个文件即可
vim mapred-site.xml
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.jobhistory.address</name>
<value>hadoop11:10020</value>
</property>
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>hadoop11:19888</value>
</property>
</configuration>
vim yarn-site.xml
<configuration>
<!-- Site specific YARN configuration properties -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.resourcemanager.hostname</name>
<value>hadoop11</value>
</property>
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<property>
<name>yarn.log-aggregation.retain-seconds</name>
<value>172800</value>
</property>
</configuration>
3.重启hdfs和Yarn服务
start-dfs.sh
start-yarn.sh