一般的 MapReduce 程序会经过以下几个过程:输入(Input)、输入分片(Splitting)、Map阶段、Shuffle阶段、Reduce阶段、输出(Final result)。
1、输入就不用说了,数据一般放在 HDFS 上面就可以了,而且文件是被分块的。关于文件块和文件分片的关系,在输入分片中说明。
2、输入分片:在进行 Map 阶段之前,MapReduce 框架会根据输入文件计算输入分片(split),每个输入分片会对应一个 Map 任务,输入分片往往和 HDFS 的块关系很密切。例如,HDFS 的块的大小是 128M,如果我们输入两个文件,大小分别是 27M、129M,那么 27M 的文件会作为一个输入分片(不足 128M 会被当作一个分片),而 129MB 则是两个输入分片(129-128=1,不足 128M,所以 1M 也会被当作一个输入分片),所以,一般来说,一个文件块会对应一个分片。Splitting 对应下面的三个数据应该理解为三个分片。
3、Map 阶段:这个阶段的处理逻辑就是编写好的 Map 函数,因为一个分片对应一个 Map 任务,并且是对应一个文件块,所以这里其实是数据本地化的操作,也就是所谓的移动计算而不是移动数据。如上图所示,这里的操作其实就是把每句话进行分割,然后得到每个单词,再对每个单词进行映射,得到单词和1的键值对。
4、Shuffle 阶段:这是“奇迹”发生的地方,MapReduce 的核心其实就是 Shuffle。那么 Shuffle 的原理呢?Shuffle 就是将 Map 的输出进行整合,然后作为 Reduce 的输入发送给 Reduce。简单理解就是把所有 Map 的输出按照键进行排序,并且把相对键的键值对整合到同一个组中。如上图所示,Bear、Car、Deer、River 是排序的,并且 Bear 这个键有两个键值对。
5、Reduce 阶段:与 Map 类似,这里也是用户编写程序的地方,可以针对分组后的键值对进行处理。如上图所示,针对同一个键 Bear 的所有值进行了一个加法操作,得到
6、输出:Reduce 的输出直接写入 HDFS 上,同样这个输出文件也是分块的。
从上图可以看到,map的数量主要来自split的数量:
逻辑如下
如果不改,用默认值的话,split size就是128M.
默认参数如下
hive> set mapred.min.split.size;
mapred.min.split.size=1
hive> set mapred.max.split.size;
mapred.max.split.size=256000000
hive> set dfs.block.size;
dfs.block.size=134217728
例如:假设input目录下有1个文件a,大小为780M,那么hadoop会将该文件a分隔成7个块(6个128m的块和1个12m的块),从而产生7个map数
假设input目录下有3个文件a,b,c,大小分别为10m,20m,130m,那么hadoop会分隔成4个块(10m,20m,128m,2m),从而产生4个map数
即,如果文件大于块大小(128m),那么会拆分,如果小于块大小,则把该文件当成一个块。
如果有很多小文件,每个小文件被当作一个块,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,这就不合理了. 属于资源浪费.
解决这个,可以对小文件进行合并处理.
举个例子
select count(1) from emp where dt='2020-9-1'
然后这个目录下有200个1m左右的小文件,那么就需要200个task.
-- 每个map最大输入大小
set mapred.max.split.size=100000000;
--节点中可以处理的最小文件大小,100M
set mapred.min.split.size.per.node=100000000;
--机架中可以处理的最小的文件大小
set mapred.min.split.size.per.rack=100000000;
//表示执行map前对小文件进行合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
reduce的数量可以自定义,map是没有直接自定义这个设置的
你说多少就是多少
hive> set mapred.reduce.tasks;
mapred.reduce.tasks=-1
没有自定义的话会有默认值
主要依据下面这两个参数
//每个reducer任务处理的数据量,本里中是256M
hive> set hive.exec.reducers.bytes.per.reducer;
hive.exec.reducers.bytes.per.reducer=256000000
//每个任务的最大reducer数量,这里是1009
hive> set hive.exec.reducers.max ;
hive.exec.reducers.max=1009
本例中
reducer数量=min(1009,总输入数据量/256M)
举个例子
select name,count(1) from emp where dt='2020-9-1' group by name;
如果map端输出的总大小是9G,那么reducer数量就是9000/256=35
要减少reducer个数,可以调大hive.exec.reducers.bytes.per.reducer=256000000
比如调大到500M,reducer数目就是18个
同map一样,启动reducer也要消耗资源和时间,另外,有多少reduce,就会有多少小文件生成,如果这些小文件作为下一个任务的输入,就会造成小文件过多. namenode的负担也会加重
有一点关系,因为reducer的数量是由map输出的数据的大小决定的.map输出的数据量越大,reduce的数量相应的也就越多.
因为会产生大量小文件,比如一个表,不分区,就是一个文件. 按一个字段,分5个区,就是5个文件, 要是二级分区,比如依据省份分30个,依据城市分100个. 则会生成30*100=3000个小文件.用mr处理的话,就是3000个map task!