导航
hadoop 面试
mr-Shuffle 机制
Map方法之后,Reduce方法之前
的数据处理过程称之为Shuffle
- Collect收集阶段:Map处理过后的数据,首先会进入入到分区方法,把数据标记好分区,然后把数据发送到环形缓冲区
(1) 环形缓冲区大小100m,一侧存索引,一侧存数据
(2)数据是以分区的形式进行存储
(3)数据到达80%时会进行反向溢写
(4)溢写之前会对数据进行排序,按照key的索引进行字典排序,排序的手段为快排
底层:
① 当数据经过逻辑处理完后,一般会调用OutputCollector.collect()输出结果。在该函数内部,它会将生成的key/value分区(调用Partitioner),然后写入一个环形内存缓冲区中
②分区数据的元信息写到内存索引数据结构SpillRecord中,其中每个分区的元信息包括在临时文件中的偏移量、压缩前数据大小和压缩后数据大小。如果当前内存索引大小超过1MB,则将内存索引写到文件output/spillN.out.index中
③具体的排序方式是,先按照分区编号Partition进行排序,然后按照key的索引进行字典排序。经过排序后,数据以分区为单位聚集在一起,且同一分区内所有数据按照key有序
- Spill(溢写)阶段:当环形缓冲区满后(80%),MapTask会将数据写到本地磁盘上,生成一个临时文件
底层:
按照分区编号由小到大依次将每个分区中的数据写入任务工作目录下的临时文件output/spillN.out(N表示当前溢写次数)中
注意:
溢写数据到临时文件时,可以将数据进行压缩
- Merge阶段:溢写完成之后,会产生大量的临时文件(因为数据达到80%时,就溢写一次),然后MapTask对所有临时文件进行归并排序,对溢写的文件也可以进行Combiner操作,前提是汇总操作,求平均值不行,最后将文件按照分区存储到磁盘,等待Reduce端拉取
底层:
① MapTask以分区为单位进行合并。对于某个分区,它将采用多轮递归合并的方式(每轮合并由参数mapreduce.task.io.sort.factor控制,默认10个文件),将多个文件合并成一个大文件,并保存到文件output/file.out中,同时生成相应的索引文件output/file.out.index
② 每个MapTask最终只生成一个数据文件
-
Copy阶段:每个ReduceTask去拉取Map端对应分区的数据,拉取过来的数据先存储到内存中,内存不够了,再存储到磁盘
-
Sort阶段:采用归并排序将内存和磁盘中的数据都进行排序。在进入Reduce方法前,可以对数据进行分组操作
注意:
由于各个MapTask已经实现对自己的处理结果进行了局部排序,因此ReduceTask只需对所有数据进行一次归并排序即可
mr-Shuffle 机制优化
1)Map阶段优化
- 增大环形缓冲区大小。由100m扩大到200m
- 增大环形缓冲区溢写的比例。由80%扩大到90%
- 增加每次merge合并次数,factor由10可以提高到20
- 不影响实际业务的前提下,采用Combiner提前合并,减少 I/O
2)Reduce阶段优化
- 设置Map、Reduce共存:调整slowstart.completedmaps参数,使Map运行到一定程度后,Reduce也开始运行,减少Reduce的等待时间
- 每个ReduceTask会去拉取自己指定分区中的数据,默认一次拉取5个分区中的数据,可调为一次拉取10个分区的数据,同时需要增加内存的大小
3)内存调整
ReduceTask默认内存1g,一般需要调整到4~6g
可参照经验公式: Reduce端每处理 128m Map端数据时,给1g内存
Map Task默认内存1g,一般不需要调整
可参照经验公式: Map 端每处理 128m 源数据时,给1g内存
4)并行度调整
MapTask并行度由输入文件切片个数目决定一般不需要调整
ReduceTask并行度参考经验公式:【Reduce端每处理 128m Map端数据时,给1g内存,2~4g内存,给1个cpu核】
5)压缩
采用压缩,减少网络IO
hadoop中支持的压缩算法:
压缩方式选择时重点考虑:
解压缩速度、压缩率、压缩后是否可以支持切片
压缩格式 | 是否支持切片 | 解压缩速度 | 压缩率 |
---|---|---|---|
snappy | no | 最快 | 很差 |
lzo | yes | 很快 | 很高 |
bzip2 | yes | 最慢 | 最高 |
gzip | no | 一般 | 很高 |
mr过程压缩建议
lzo压缩算法的缺点:
- 需要上传jar包
- 需要手动为文件创建索引,没有索引不支持文件切片
所有压缩算法的缺点:
加重CPU负荷,算法越复杂,解压时间越长
疑问?
是为了方便后续的merge过程,数据有序的情况下,merge的速度非常快
分区是为了将数据送往到不同的reducerTask当中,10个reducerTask处理数据的速度肯定比1个快
因为前边已经已经对数据按照分区进行了快速排序