首页 > 代码库 > Hive参数层面优化之一控制Map数

Hive参数层面优化之一控制Map数

1、Map个数的决定因素

通常情况下,作业会通过input文件产生一个或者多个map数;

Map数主要的决定因素有: input总的文件个数,input文件的大小和集群中设置的block的大小(在hive中可以通过set dfs.block.size命令查看,该参数不能自定义修改);

文件块数拆分原则:如果文件大于块大小(128M),那么拆分;如果小于,则把该文件当成一个块

 

举例一:

假设input目录下有1个文件a,大小为780M,那么hadoop会将该文件a分隔成7个块(6个128m的块和1个12m的块,Block是128M),从而产生7个map数;

 

实例二:

假设input目录下有3个文件a,b,c,大小分别为10m,20m,130m,那么hadoop会分隔成4个块(10m,20m,128m,2m),从而产生4个map数;

 

两种方式控制Map数:即减少map数和增加map

减少map数可以通过合并小文件来实现,针对文件源;

增加map数的可以通过控制上一个job的reduer数来控制(一个sql中join多个表会分解为多个mapreduce),因为上一个job的reduce输出数决定了这个job的map数;

 

2、Map数过大或过小引发的问题

Map数过大将导致:

1)  Map阶段输出文件太小,产生大量小文件;当下一个阶段被使用时,需要合并小文件;多个小文件如果被reduce处理的话,就需要有很多个reduce数;

2)  初始化和创建Map的开销很大;

3)  如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时一个JOB可执行的map数也是受限的

 

Map数太小将导致:

1)  文件处理或查询并发度小,Job执行时间过长;

2)  大量作业时,容易堵塞集群;

3)  频繁推测执行;

 

是不是保证每个map处理接近128M的文件块,就高枕无忧了?

答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。

 

3、设定map数:mapred.map.tasks

当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率;

hive中map数默认值是2;

 

案例环境描述:

hive.merge.mapredfiles=true (默认是false,可以在hive-site.xml里配置,在整个job执行后合并)

hive.merge.mapfiles=true  map执行后合并

hive.merge.size.per.task=256000000

mapred.map.tasks=2   hive中map数默认是2

因为合并小文件默认是true,而dfs.block.size与hive.merge.size.per.task的搭配使得合并后的绝大部分文件都在256M左右;

 

案例一:

现在我们假设有3个300MB大小的文件,每个BLOCK大小为256M而且不设定map数;

整个JOB会有6个map,其中3个map分别处理256MB的数据,还有3个map分别处理44MB的数据;

木桶效应就来了,整个JOB的map阶段的执行时间不是看最短的1个map的执行时间,而是看最长的1个map的执行时间。所以,虽然有3个map分别只处理44MB的数据,可以很快跑完,但它们还是要等待另外3个处理256MB的map。显然,处理256MB的3个map拖了整个JOB的后腿。

 

案例二:

如果我们把mapred.map.tasks设置成6,再来看一下有什么变化:

goalsize = min(900MB/6,256MB) = 150MB

整个JOB同样会分配6个map来处理,每个map处理150MB的数据,非常均匀,谁都不会拖后腿,最合理地分配了资源,执行时间大约为CASE 1的59%(150/256)

 

案例三:

select data_desc, count(1), count(distinct id), sum(case when …), sum(case when ...),  sum(…) from a group by data_desc

 

如果表a只有一个文件,大小为120M,但包含几千万的记录,如果用1个map去完成这个任务,肯定是比较耗时的,这种情况下,我们要考虑将这一个文件合理的拆分成多个,这样就可以用多个map任务去完成。

set mapred.reduce.tasks=10;create table a_1 as select * from a  distribute by  rand(123); 

 

这样会将a表的记录,随机的分散到包含10个文件的a_1表中,再用a_1代替上面sql中的a表,则会用10个map任务去完成。 每个map任务处理大于12M(几百万记录)的数据,效率肯定会好很多。

 

4、Map数总结

 看上去,貌似这两种有些矛盾,一个是要合并小文件,一个是要把大文件拆成小文件,这点正是重点需要关注的地方, 根据实际情况,控制map数量需要遵循两个原则:

1)   使大数据量利用合适的map

2)   使单个map任务处理合适的数据量