简介
一、概述
- MapReduce是Hadoop提供的一套用于进行分布式计算的框架
- 将计算过程拆分为2个阶段:Map(映射)阶段和Reduce(规约)阶段
组件
一、序列化
- 在MapReduce中,要求数据能够被序列化
- MapReduce的序列化机制默认采用的AVRO
- MapReduce对AVRO的序列化机制进行了封装,提供了更简便的序列化形式 - 实现接口Writable
二、分区 - Partition
- 分区的作用是将数据进行分类
- 有无分区并不影响Map和Reduce的执行逻辑
- 分区默认是从0开始依次递增的
- 在MapReduce中,每一个分区要对应一个ReduceTask,每一个ReduceTask都会产生一个结果文件。默认情况下只有1个分区,也就只有1个ReduceTask,只产生一个结果文件
- 在MapReduce中,如果没有手动指定Partitioner,那么默认使用的分区类是HashPartitioner
三、排序
- 在MapReduce中,自动对键进行排序
- 要求键所对应的类必须实现Comparable接口,但是考虑到键需要序列化,所以一般实现的WritableComparable
- 在排序过程中,如果compareTo方法的返回值为0,则MapReduce会认为这两个键是同一个,则将这两个键的值放到一组,相当于去重过程
- 练习:按照月份升序排序,如果月份一样,则按照利润降序排序(文件:profit2.txt)
四、合并 - Combine
- 大多数情况下,MapTask的数量要远远多于ReduceTask的数量,导致计算压力几乎全部落在了ReduceTask上,ReduceTask的计算效率就成为整个MapReduce的瓶颈
- 合并的逻辑和Reducer的逻辑是一样的 - 只需要在Driver中添加job.setCombinerClass(Reducer.class);
- 合并的特点:减少数据总量但是不改变计算结果
- 如果进行汇总、获取最值、去重之类操作可以使用Combiner,但是例如求平均之类的操作不能使用Combiner
细节
一、数据本地化策略
-
当JobTracker收到MR程序的时候,会访问NameNode获取文件信息。文件信息包含文件大小以及块信息
-
JobTracker对这个文件进行切片处理。注意:切片是逻辑切分不是物理切分。切片数量决定了MapTask的数量。默认情况下,Split和Block是等大的
-
JobTracker会将划分出来的MapTask分配到TaskTracker上执行
-
因为MapTask在执行过程中需要读取数据,而数据在DataNode上,所以将DataNode和TaskTracker部署在相同的节点上以减少跨集群的网络传输
-
为了减少网络资源的消耗,在分配任务的时候会考虑Block的位置。哪个节点上有要处理的数据,将任务分配给哪个节点,这个过程称之为数据本地化
-
切片产生过程:
a. 如果文件为空,则整个文件作为一个切片处理
b. 在MapReduce中,文件要区分可切或者不可切,例如绝大部分的压缩文件就是不可切的
c. 如果文件不可切,则整个文件作为一个切片处理
d. 如果需要减小splitsize,需要调小maxsize;如果需要调大splitsize,需要调大minsize
e. 在计算切片的时候,需要考虑切片阈值 - SPLIT_SLOP = 1.1
二、MR执行流程
- 准备阶段:
a. 检查输入和输出路径
b. 计算切片数量
c. 如果有必要,设置缓存存根
d. 将jar包和配置上传到HDFS上
e. 将任务提交给JobTracker,并且可以选择是否监控这个任务 - 执行阶段:
a. JobTracker收到Job任务之后,会将这个任务进行拆分,拆分成MapTask和ReduceTask。MapTask的数量由切片决定;ReduceTask的数量由分区数量决定
b. JobTracker在拆分完成任务之后,会等待TaskTracker的心跳,然后给TaskTracker分配任务。分配任务的时候,MapTask尽量满足数据本地化策略,ReduceTask无法满足数据本地化,所以ReduceTask在分配的时候是考虑节点的空闲
c. TaskTracker通过心跳领取任务,领取到任务之后,会去对应节点上下载jar包,这一步体现的思想是逻辑移动数据固定
d. TaskTracker会在本节点上开启JVM子进程执行MapTask或者ReduceTask。注意:每一个MapTask或者ReduceTask的执行都会开启一次JVM子进程
Shuffle
一、Map端的Shuffle
- map方法在处理完成数据之后会将结果写出到MapTask自带的缓冲区中 - 每一个MapTask自带一个缓冲区 - MapOutputCollector
- 数据在缓冲区中进行分区、排序,如果指定了Combiner,那么数据在缓冲区中还会进行combine。注意:在缓冲区中的排序是将完全无序的数据整理成有序数据,采取的是快速排序
- 缓冲区是维系在内存中,默认是100M
- 当缓冲区的使用达到一定限度(溢写阈值:0.8)的时候,会将缓冲区中的数据溢写(spill)到磁盘上,map方法后续产生的结果会继续写到缓冲区中
- 每一次溢写都会产生一个新的溢写文件 - 单个溢写文件中的数据是分区且有序的,所有的溢写文件之间是局部有序的
- 在map方法完成之后,将所有的溢写文件进行合并(merge),将所有的溢写文件合并成一个结果文件(final out),在merge过程中,数据会再次进行分区排序 - final out是整体分区且有序的。merge过程中的排序是将局部有序变成整体有序,所以采用的是归并排序
- 如果map方法执行完成之后,缓冲区中依然有数据,则会直接合并到最后的final out中
- 在merge过程中,如果spill文件个数>=3并且指定了Combiner,则在merge的时候会再进行一次combine
- 注意问题:
a. spill过程不一定产生
b. 默认情况下,溢写文件的大小不一定是80M,考虑序列化因素
c. 缓冲区本质上是一个环形的字节数组,设置为环形的目的是为了避免寻址,能够重复利用缓冲区
d. 阈值的作用是为了减少写入的阻塞
二、Reduce端的Shuffle
- ReduceTask启动多个fetch线程去MapTask处抓取对应分区的数据
- ReduceTask将从每一个MapTask上抓取过来的数据存储在一个本地文件中
- 将抓取来数据进行一次merge,合并成一个大文件,在merge过程中,会再次进行排序,采用的是归并排序
- merge完成之后,ReduceTask会再将相同的键对应的值放到一个迭代器中,这个过程称之为分组(group)
- 分组完成之后,每一个键对应一个迭代器,每一个键调用一次reduce方法
- 注意问题:
a. ReduceTask的启动阈值:0.05 - 当5%的MapTask结束,就会启动ReduceTask去抓取数据
b. fetch线程通过HTTP请求获取数据
c. fetch线程的数量默认为5
d. merge因子:10 - 每10个小文件合并成1个大文件
三、Shuffle的调优
- 减少溢写次数:
a. 增大缓冲区,实际过程中缓冲区的大小一般是在250~400M之间
b. 增大缓冲区阈值,同时增加了写入阻塞的风险 - 不建议
c. 增加Combine的过程 - 可以考虑将Map的结果文件进行压缩,这个方案是在网络资源和CPU资源之间的取舍
- 增加fetch线程的数量
- 增大merge因子,会增加底层计算的复杂度 - 不建议
- 减小ReduceTask的启动阈值,增加了ReduceTask的阻塞风险 - 不建议
InputFormat
一、概述
-
InputFormat中定义了2个抽象方法:
a. getSplits用于产生切片
b. createRecordReader产生输入流读取切片 -
InputFormat会把结果给到MapTask
-
实际过程中,如果需要自定义输入格式类,一般不是直接继承InputFormat而是继承它的子类FileInputFormat,这个子类中已经覆盖了getSplits方法,而只需要考虑如何读取数据即可
-
如果没有指定输入格式,那么默认使用的TextInputFormat。除了第一个切片对应的MapTask意外,其余的MapTask都是从当前切片的第二行开始读取到下一个切片的第一个行
-
多源输入下,允许输入不同格式的文件,但是文件格式可以不同Mapper类也可以不一样,但是最后交给Reducer处理的时候要一样
其他细节
一、数据倾斜
- 数据本身就有倾斜特性,即日常生活中所产生的数据本身就是不均等的
- 实际过程中,绝大部分的数据倾斜都会产生在Reduce端
- Map端产生倾斜的条件:多源输入、文件不可切且文件大小不均 - Map端的倾斜一旦产生无法解决 - 如果真的要解决,在特定条件下可以考虑缓存存根问题
- Reduce端的倾斜的本质是因为数据的倾斜性,但是直观原因是因为对数据进行了分类 - 分类规则往往是不可变的,所以在实际过程中往往考虑的是使用两阶段聚合 - 数据先打散后聚合
二、小文件
- 小文件的危害:
a. 存储:大量小文件会产生大量的元数据,就导致内存被大量占用
b. 计算:大量小文件就产生大量的切片,大量切片则意味着有大量的MapTask,会导致服务器的执行效率变低甚至会导致服务器崩溃 - 针对小文件的处理手段常见的有2种:合并和压缩
- Hadoop提供了一种原生的合并手段:Hadoop Archive,将多个小文件打成一个har包