学习的时候没有太过注意Shuffle这个概念,以至于还认为是不是漏掉了什么知识点,前面看了一些帖子才发现Shuffle原来是map()办法执行完结到reduce()办法执行这么一大段过程....
小声BB:本质上该过程蕴含许多环节,不晓得为啥就统称Shuffle了......
1. Mapper的写出
图见意现 -- 间接上图:
须要明确的点:
- 在环形缓冲区触发溢写时,进行的排序是先依据每个键值对的分区进行排序,而后再依据key进行排序
- 每次写出实际上包含两个文件,一个是meta数据写出造成的index文件,另一个则是排序后的data文件
- 在溢写时产生一次排序,为疾速排序算法;在最初合并为一个整体文件时,因为各小文件已有序,间接归并排序即可
- Reducer从Mapper节点的最终文件file.out读取数据,依据index文件只读取对应分区的数据
2. Mapper的分区
分区的作用是将Mapper阶段输入的后果分为不同的区域,并传送给不同的ReduceTask,因而分区数和Reduce的并行度是互相决定的,即 Num Of Partitions = Num Of ReduceTask
设置ReduceTask数量:job.setNumReduceTasks(n)
分区形式:
- 默认形式:hash(key) % Num of ReduceTask -- 键值哈希取模
- 自定义分区类:定义类继承Partitioner类,并重写getPartition()办法
留神:
- 在没有设置ReduceTask数目的状况下,MR默认只调配一个ReduceTask,即不论自定义分区办法须要分几个区,最初的后果都只会进入这一个Reducer,最终生成一个后果文件;
- 如果设置了ReduceTask数量大于1,但该数量小于自定义的分区数,则运行MR程序会报错,因为会产生一些Mapper的后果数据没有Reducer来获取的状况
- 如果设置的分区数 < ReduceTask的数量,那么程序会失常执行,最初也会生成和ReduceTask数相等的后果文件,但其中有 (ReduceTask数 - 分区数) 个空文件,因为这部分的Reducer没有输出
3. Mapper的排序
- 比较简单,溢写时疾速排序,合并时归并排序;排序规范为对key进行字典排序
- 可自定义排序(在自定义Bean对象时应用),须要让自定义的Bean类实现WritableComparable接口并重写compareTo办法,实际上和Java中的Comparable根本一样
4. Reducer的读入
须要明确的点:
- 一个ReduceTask只会读取每个Mapper后果的同一个分区内的数据
- 拷贝到Reducer节点先是保留在内存中,如果内存不够就刷写到磁盘
- 所有数据拷贝实现后,对内存和磁盘(如果产生了刷写)进行Merge && Sort,这里排序也是默认依据Key进行字典排序,目标是让数据中key雷同的KV对排列在一起,不便reduce办法的调用。【reduce办法调用前会将所有key雷同的value封装为iterator迭代器对象,对于一个key调用一次reduce办法】
5. Reducer的排序
Reduce端的排序同样也是利用于应用了自定义Bean对象的场景,通过自定义的排序形式让Reducer把咱们指定的keys判断为同样的key(在默认reduce排序中会被断定为非同key)
留神!留神!留神!此处有坑!!!
- Reducer端的自定义排序须要自定义排序类,继承WritableComparator并重写compare办法,并在Driver中关联咱们自定义的排序类
- Mapper端的自定排序须要在咱们定义Bean对象的时候让Bean对象继承WritableComparable类并重写办法CompareTo