此MR程序以wordcount为例
1、 数据准备:如图所示,在HDFS中有一个1G的文件,HDFS中的的默认分块的大小为256M,分别存储在HDFS中的四个块中。在文件被分成块之后,不会直接被mapreduce处理。而是先由FileInputFormat进行切片。
2、 在MR程序运行的过程中,会生成许多MapTask程序,MapTask的数量和“分片”的数量相同,一般来说,每个分片都有一个MapTask来执行。之所以进行分片,原因在于分片之后可以调整处理这个块的MapTask的数量。例如,当一个块比较大,而我们不想让一个MapTask处理这个块,而是由两个MapTask去处理。这时候可以把分片的大小调整为原来分块大小的二分之一。有一种特殊情况,即在分片的过程中,剩余文件大小 小于等于块大小的110%,会只切割成一个分片,因此,一个分片最大可以为一个块大小的110% 。
3、 分片的大小默认等同于HDFS块的大小,不能设置为比HDFS的块大(源码限制),如果要自定义,就要设置其大小为HDFS块大小的n分之1,否则可能会引起数据转移。
4、 在程序中具体是如何实现分片的,在MR程序中,在编写Job的过程中,我们会发现,有一个FileInputFormat,即输入格式化器,该类继承了InputFormat,继承该类的还有DBInputFormat等,该类主要负责的就是对HDFS上的数据进行分片,但注意,分片不是物理上的分片,而是逻辑上的划分,这是对我们要处理的数据,最后一次的抽象。
5、 FileInputFormat在分片时,只要知道HDFS上块的元信息即可,记录下开始的位置和结束的位置以及长度即可。分片会形成一个分片的数组,因为我们的分片数量不确定,所以用一个数组存储分片最为稳妥。
6、 分片之后我们就可以进行读取分片了,读取分片使用的默认类叫做LineRecordReader,即行记录读取器,他会从分片上(实际上还是分片对应的文件块上)一行一行读取 数据到MapTask中,如果读取到分片的最后一个字符且这个字符不是换行符,他会去下一个分片中去读取,直到读取到第一个换行符为止。这个分片上的MapTask就结束了。
7、 Mapper指的是我们自定义的应用程序的类,MapTask指的是Mapper执行之后的程序。
8、 在每个MapTask中,首先执行setUp方法(执行一次),然后是循环执行map方法(map()方法,执行次数不固定,和行数有关),最后执行的是cleanUp()方法(只执行一次)。
**补充1:**ReduceTask的数量由谁决定
事实上ReduceTask的数量是很难确定的,这跟具体业务中的key相关,但是这是有个极限的。如果在我们的业务中只有三个不重复key, 即使我们设置了四个Reducer进程,这样也是无效的,因为只有三个Key,总有一个Reducer会接收不到数据,无事可做,显然这样的设置是无用的。但如果我们的业务中有很多Key,这样我们手动设置合理的ReduceTask的数量这样就是有意义的。总结一句话,ReduceTask的数量由具体的业务有直接的关系。
**补充2:**如何解决数据倾斜的问题
如:我们有一个文件,大小为1T,该文件只有两个key,一个key中的数据有999G,另一个key中有1个G,这样我们在做WordCount时,就会造成一个ReduceTask任务过少,一个ReduceTask中的数据过多,但是任务根本无法完成。
这时我们可以采用自定义业务Key的方式解决这个问题,具体的实现方法如下:
通过生成随机数拼接带999G数据的key上,如随机数的数量为1000个(如0-999),通过这种方式,我们就生成了1000个不同的key,这时再做Reducer的时候,就不会出现数据倾斜了。