MapReduce秒懂
MapReduce定义
MapReduce是一个分布式运算程序的编程框架
MapReduc优缺点
优点
- 易于编程
- 良好的扩展性
- 高容错性
- 适合PB级以上海量数据的离线计算
缺点
- 不擅长实时计算
- 不擅长流式计算
- 不擅长DAG(有向图)计算
MapReduc核心思想
- Job(作业) : 一个MapReduce程序称为一个Job,MR程序一般需要分成2个阶段:map阶段和reduce阶段
- MRAppMaster:MR任务的主节点,一个Job在运行时,会先启动这个进程,负责Job中执行状态的监控、容错、RM申请资源、提交Task等等
- Map是MapReduce程序运行的第一个阶段,MapTask负责是Map阶段程序的计算,在一个MR程序的Map阶段,会启动N个MapTask,每个MapTask是完全并行运行,互不相干
- Reduce是MapReduce程序运行的第二个阶段(最后一个阶段),Reduce阶段的目的是将Map阶段,每个MapTask计算后的结果进行合并汇总,数据依赖于上一个阶段的所有MapTask并发实例的输出,ReduceTask也是并行运行,每个ReduceTask最终都会产生一个结果
- MapReduce程序只能包含一个Map阶段和一个Reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个MapReduce程序,串行运行
MapReduc工作流程
Map阶段
切片(split)
切片指的是MapReduce框架根据输入数据源的大小和配置参数,将数据源分割成多个较小的数据集合,每个数据集合称为一个切片(Split),每个切片会被分配给一个单独的MapTask进行处理
InputFormat
MR程序必须指定一个输入目录,一个输出目录,InputFormat代表输入目录文件的格式。默认的是普通文件,使用FileInputFormat,如果处理的数据在数据库中,需要使用DBInputFormat
FileInputFormat
用来读取数据,其本身为一个抽象类,继承自 InputFormat 抽象类,针对不同的类型的数据有不同的子类来处理,不同的子类有着不同的切片机制,常见的接口实现类如下:
-
TextInputFormat(默认)
切片机制: 1. 通过计算文件的起始位置、文件的长度以及配置的块大小,简单的按照文件的内容长度进行切片 2. 切片大小,默认等于Block大小(blocksize) 3. 切片时不考虑数据集整体,而是逐个针对每一个文件单独切片 4. 每次切片时,都要判断切完剩下的部分是否大于块的1.1倍,不大于1.1倍就划分为一块切片
-
KeyValueTextInputFormat
按行读取,每一行为一条记录,被分隔符分隔,默认分隔符为'\t'
-
NLineInputFormat
切片机制按照指定的行数N来划分,即输入文件的总行数/N=切片数,不能整除则+1片
-
CombineTextInputFormat
用于小文件过多的场景,它可以将多个小文件从逻辑上规划到一个切片中,这样多个小文件就可以交给一个MapTask处理 切片机制: 包括虚拟存储过程和切片两个部分 虚拟存储过程: 1. 将输入目录下所有文件按照文件名称字典顺序一次读入,记录文件大小,并累加计算所有文件的总长度 2. 根据设置的虚拟存储切片最大值,将每个文件划分成一个一个设置的切片值大小的文件 3. 当剩余数据大小超过设置的切片值且不大于2倍时,将文件均分成2个虚拟存储块(防止出现太小切片) 切片过程: 1. 判断虚拟存储的文件大小是否大于设置的切片值,大于等于则单独形成一个切片 2. 如果不大于则跟下一个虚拟存储文件进行合并,共同形成一个切片 举例: text1大小2M、text2大小5M、text3大小3M、text4大小6M 设置的虚拟存储切片最大值为4M 虚拟存储过程: text1:2M < 4M 划分为1块 text2:4M < 5M < 2 * 4M 划分为2块,块1:2.5M,块2:2.5M text3:3M < 4M 划分为1块 text4:6M < 5M < 2 * 4M 划分为2块,块1:3M,块2:3M 最终虚拟存储过程划分的块数为6块,2M、2.5M、2.5M、3M、3M、3M 切片过程: 没有文件大小是大于4M的,所以俩俩合并共同形成一个切片,最终切片为3块 2 + 2.5 = 4.5M 2.5 + 3 = 5.5M 3 + 3 = 6M
-
自定义InputFormat
FileInputFormat切片机制
- 通过计算文件的起始位置、文件的长度以及配置的块大小,简单的按照文件的内容长度进行切片
- 切片大小,默认等于Block大小(blocksize)
- 切片时不考虑数据集整体,而是逐个针对每一个文件单独切片
FileInputFormat切片流程
- 程序先找到你数据存储的目录
- 开始遍历处理目录下的每一个文件
- 遍历第一个文件,获取文件的大小,计算设置切片的大小(默认是块大小128M),开始切片
- 将切片信息写到一个切片规划文件中
- 提交切片规划文件到YARN上,YARN的MRAppMaster根据切片规划文件计算开启MapTask个数
读取(RecordReader)
RecordReader负责从输入格式中,读取数据,读取后封装为一组记录(k-v),mapreduce只能处理kv
处理(mapper)
将解析出的key_value交给map()函数处理,并产生一系列新的key_value
收集(collect)
收集线程负责将写出的key-value收集到缓冲区(MapOutPutBuffer)中,每个记录在进入缓冲区时,先调用Partitioner(分区器)为记录计算一个区号
- 缓冲区默认大小为100M
溢写(spill)
溢写线程会在缓冲区已经收集了80%空间的数据时被唤醒,唤醒后负责将缓冲区收集的数据溢写到磁盘上,生成一个临时文件
溢写流程
- 一旦缓冲区满足溢写条件,先对缓冲区的所有数据,进行一次排序,排序方式是先按照分区编号Partition进行排序,然后按照key进行升序排序,排序时,只排索引不移动数据,经过排序后,数据以分区为单位聚集在一起,且同一分区内所有数据按照key有序
- 按照分区编号由小到大进行溢写,将每个分区中的数据写入任务工作目录下的临时文件中,溢写多次,生成多个临时文件,如果最后一批数据不满足溢写条件会执行一次flush
- 将分区数据的元信息写到内存索引数据结构SpillRecord中,其中每个分区的元信息包括在临时文件中的偏移量、压缩前数据大小和压缩后数据大小
合并(combine)
溢写结束后,MapTask对所有临时文件进行一次合并,以确保最终只会生成一个数据文件。因为后续reduce会读取文件,这样可避免同时读取大量文件产生的开销
合并流程
- MapTask以分区为单位进行合并。对于某个分区,它将采用多轮递归合并的方式
- 每轮合并10(默认)个文件,并将产生的文件重新加入待合并列表中
- 对文件进行排序,重复以上过程,直到最终得到一个大文件,这个文件每个分区中的key-value都是有序的
- 最终将数据写入到MapTask磁盘的某个文件中
Reduce阶段
拷贝(copy)
ReduceTask从各个MapTask上远程拷贝数据,每个ReduceTask只负责一个分区,所以只copy不同MapTask上这个分区的数据,针对某一片数据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中
合并(merge)
在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多
- 文件大小超过一定阈值,则放到磁盘上,否则放到内存中
- 磁盘上的文件数目达到一定阈值,进行一次合并,生成一个更大的文件
- 内存中文件大小或者数目超过一定阈值,进行一次合并后将数据写到磁盘上
排序(sort)
由于数据是从多个MapTask上copy过来的,所以要对所有数据进行一次归并排序,保证在进入reduce之前是排好序的
处理(reducer)
将排好序的数据进行分组,调用reduce()函数处理数据,将计算结果写到文件上
分区概述
分区是在MapTask中通过Partitioner来计算分区号
- 总的partitions(分区数),取决于用户设置的reduceTask的数量
- partitions>1,默认尝试获取用户设置的分区数,如果用户没有定义,那么会使用HashPartitioner,HashPartitioner根据key的hashcode进行计算,相同的key以及hash值相同的key会分到一个区
- partitions<=1,默认初始化一个Partitioner,这个Partitioner计算的所有的区号都为0
排序概述
排序是MapReduce框架中最重要的操作之一,MapTask和ReduceTask均会对数据按照key进行排序,该操作属于Hadoop的默认行为,任何应用程序中的数据均会被排序,而不管逻辑上是否需要。默认排序是按照字典顺序排序,且实现该排序的方法是快速排序
排序分类
-
部分排序
MapReduce根据输入记录的键值对数据集排序,最终生成N个结果文件,每个文件内部整体有序
-
辅助排序
在进入reduce阶段时,通过比较key是否相同,将相同的key分为一组
-
全排序
对所有的数据进行排序,指生成一个结果文件,这个结果文件整体有序
-
二次排序
在对key进行比较时,比较的条件为多个