合理设置Map任务个数
1. 一般情况下,作业会根据input目录产生一个或者多个map任务。
map任务的个数主要由如下因素决定
a. input文件总个数
b. input文件的大小
c. 集群设置的文件块大小 (目前为128M,该参数不能自定义修改)
2. 请举例说明文件切分方式
a. 假设input目录下有1个文件 c , 其大小为680M, hadoop会将该文件 c 切分为6个块(1个40M的和5个128M大小的块),对应的map数为6。
b. 假设input目录下有4个文件a , b , c, d , 它们的大小分别为5M, 10M, 128M, 140M,那么hadoop会将其切分为5个块(5个块的大小分别为5M, 10M, 128M, 128M, 12M) ,对应的Map数是,即如果文件大于块大小(128M),会进行拆分,如果小于块大小,则把该文件当成一个块进行处理。
3. map数是否越多越好?
map数并非越多越好,如果一个任务包含很多小文件(远远小于所设置的块大小),那么每个小文件都会被被当做一个独立的块且对应一个map。在上面这种情况下,map任务启动和初始化的时间远远大于逻辑处理的时间,造成很大的资源浪费。
4. 是不是保证每个map处理接近128M的文件块,就可以达到最好的性能 ?
答案是不一定。例如一个120m的文件,正常情况下会用一个map去读取,如果这个文件块内每行只有一个或者两个小字段,但有数千万级别的记录,如果这个时候map处理的逻辑比较负责的话,用一个map任务去做处理,任务整体执行耗时还是不小的。
针对3, 4 所描述的问题,可以通过如下两种方式来解决:
a. 减少map数
如何合并小文件,减少map数?
假设一个SQL任务:
select count(1) from tmp1 where pt='2020-10-10'
该任务的inputdir /xxx/pt=2020-10-10共有300个文件,其中很多是远远小于128M的小文件,总大小10G,正常执行时会启动300个map任务。
Map总共消耗的计算资源数 SLOTS_MILLIS_MAPS= 623,020。
可以通过配置如下参数来在map执行前合并小文件,减少map数:
set mapred.max.split.size=100000000;
set mapred.min.split.size.per.node=100000000;
set mapred.min.split.size.per.rack=100000000;
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
再执行上面的语句,用了74个map任务,map消耗的计算资源:SLOTS_MILLIS_MAPS= 333,500。
对于这个简单SQL任务,执行时间上可能差不多,但节省了一半的计算资源。
上面的参数的值为100000000 对应100MB
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; 这个参数表示执行前进行小文件合并,前面三个参数确定合并文件块的大小,大于文件块大小128m的,按照128m来分隔,小于128m,大于100m的,按照100m来分隔,把那些小于100m的(包括小文件和分割大文件剩下的)进行合并,最终生成了74个块。
如何适当的增加map数?
当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。
假设有这样一个任务:
select age,
count(1),
count(distinct user_id),
sum(case when …),
sum(…)
from tmp2 group by age。。
如果表a只有一个文件,大小为120M,但包含几千万的记录,如果用1个map去完成这个任务,肯定是比较耗时的,这种情况下,我们要考虑将这一个文件合理的拆分成多个,这样就可以用多个map任务去完成,具体实现样例如下:
set mapred.reduce.tasks=10;
create table tmp3 as
select * from a
distribute by rand(20);
这样会将a表的记录,随机的分散到包含20个文件的tmp3表中,再用tmp3 代替上面sql中的a 表,则会用20个map任务去完成。每个map任务处理6M左右(数百万记录)的数据,效率会提高不少。
增加Map数与减少Map数,一个是要把大文件拆成小文件,一个是要合并小文件,听起来是矛盾的,但是这个也正是需要开发者重点关注的地方,开发者需要根据实际的数据情况, 合理的控制Map的个数。整体上要保证单个map任务处理的数据量适中且任务数据吞吐量合理。
合理设置reduce任务个数
- Hive自己如何确定reduce数?
reduce个数的设定极大影响任务执行效率,不指定reduce个数的情况下,Hive会猜测确定一个reduce个数,基于以下两个设定:
hive.exec.reducers.bytes.per.reducer -- (每个reduce任务处理的数据量,默认为1000^3=1G)
hive.exec.reducers.max -- (每个任务最大的reduce数,默认为999)
Reduce 个数 N = min(参数2,总输入数据量/参数1)
即,如果reduce的输入(map的输出)总大小不超过1G, 那么只会创建一个reduce任务;
2. 如何调整reduce个数?
a. 调整hive.exec.reducers.bytes.per.reducer参数的值;
set hive.exec.reducers.bytes.per.reducer=500000000; (500M)
b. set mapred.reduce.tasks = 15;
如何在命令中明确配置reduce个数,那么hive就不会推测reduce个数,而是直接创建15个reduce任务。
3. reduce个数是不是越多越好?
答案是否定的,与map一样, reduce的启动同样会耗费时间与计算资源。 另外reduce的个数决定着输出文件个数,如何reduce处理之后生成了很多小文件,如果不经过处理就传递给下游的话,又会出现小文件过多的问题。
4. 什么场景下只会创建单个reduce?
除了数据量小于 hive.exec.reducers.bytes.per.reducer 参数所设定的值之外,还有如下几个原因会导致只创建了一个reduce任务,在如下的几个原因:
a. 汇总SQL中忽略group by 算子
下面两个SQL 得到的统计量结果是相同的,但是执行效率是不同的。其中SQL1由于没有group by 算子,故进行数据量统计时,只会使用一个reduce任务进行执行。
SQL1: select count(1) from table where dt= '2020-10-04';
SQL2: select dt,count(1) from table where dt= '2020-10-04' group by dt;
上面这种情况大家经常会遇到,一定要合理的改写。
b. 使用了Order by
由于order by 会做全局的排序,只会创建一个reduce任务.
c. 出现笛卡尔积
上面这些情况下,尽量避免,由于上面这些操作都是全局的,所以hadoop不得不用单个reduce去完成。
另外,在设置reduce个数的时候同样需要考虑如下两个原则:即大数据量情况下配置合适的reduce数;单个reduce任务处理合适的数据量。