优化手段:

合理控制map和reduce数

合并小文件

避免数据倾斜,解决数据倾斜

减少job数(合并job,大job分拆。。)一个job就是一个mapreduce


map数:

map数过大会导致:

map阶段输出文件太小,产生大量小文件,所以下一阶段就需要小文件合并,浪费很多reduce数

初始化和创建map的开销很大

map数太小:

文件处理或查询并发度小,job执行时间过长

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


map数如何决定:

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

主要决定因素:input的文件数,input的文件大小


举例:

假设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的reducer数来控制(一个sql中join多个表会分解为多个mapreduce)


小文件合并:

Map阶段hive自助对小文件合并(从0.7之后)


对应参数和默认值:

set hive.merge.mapfiles = true #在Map-only的任务结束时合并小文件

set hive.merge.mapredfiles = true #默认是false,true时在map-reduce的任务结束时合并小文件

set hive.merge.size.per.task = 256*1000*1000 #合并后文件的大小

set mapred.max.split.size = 256000000; #每个Map最大分割大小,这个不是hive的,而是hadoop的

set mapred.min.split.size.per.node = 100000000; #一个节点上split的最小值

set hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; #执行Map前进行小文件合并


set的作用域是session级别

在开启了org.apache.hadoop.hive.ql.io.CombineHiveInputFormat后,一个datanode节点上多个小文件会进行合并,合并文件数由mapred.max.split.size限制的大小决定。mapred.min.split.size.per.node决定了多个datanode上的文件是否需要合并。


参数:mapred.map.tasks

hive中set.mapred.map.tasks=100;

案例:

环境如下:

hive.merge.mapredfiles = true(默认是false,可以在hive-site.xml里面配置)

hive.merge.mapfiles = true

hive.merge.size.per.task = 256000000

mapred.map.tasks = 2


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


case1

现在我们有三个300MB大小的文件

整个job会有6个map,其中三个map分别处理256MB的数据,还有三个map分别处理44mb的数据。

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


case2

如果我们把mapred.map.tasks设置为6,(因为它默认是2,也就是它的最小值是2),再来看一下有什么变化:

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

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


reduce数

reduce数过大

生成了很多小文件(最终输出文件由reduce决定,一个reduce一个文件),那么如果这些小文件作为下一个job输入,则会出现小文件过多需要进行合并的问题。

消耗大量不必要的资源

创建reduce消耗大量资源

reduce数过低

执行耗时

可能出现数据倾斜-比如说map有100个,而reduce只有一个

启动和初始化reduce也会消耗时间和资源,有多少个reduce就会有多少输出文件


reduce数的决定因素:

默认下,hive分配reduce个数基于以下:

参数1:hive.exec.reducers.bytes.per.reduce(默认为1G)

参数2:hive.exec.reducers.max(默认为999)

计算reducer数的公式:

N=min(参数2,总输入数据量/参数1)

也就是默认一个reduce处理1G数据量


什么情况下只有·一个reduce

很多情况任务中不管数据量多大,不管你有没有设置reduce个数的参数,任务中一直都只有一个reduce任务,也就是数据倾斜一个很常见的现象

原因有可能:

1,有时数据量小于hive.exec.reducers.bytes.per.reducer参数值,也就是1个G

2,用了group by也会很容易数据倾斜

3,用了order by


设置reduce数:

参数:mapred.reduce.tasks

默认:1


set.mapred.reduce.task = 10

作用域是session


当某个job的结果被后面job多次引用,设大参数,以便增大访问的map数

hadoop fs -du /user/hive/ 这个命令会统计这个目录下有多少数据量

如果这个表经常访问,我们就在表sql文件中头部加上set mapred.reduce.tasks = 10;来增加并发度


数据倾斜

1为什么是数据倾斜,hadoop框架的特性决定了最怕数据倾斜,因为它实际上就是jobtracker和tasktrackers,他们实际上就是老师和学生们的关系

由于数据分布不均匀,造成数据大量的集中到一点,造成数据热点

可能产生的症状(后果):

map阶段快,reduce阶段非常慢

某些map很快,某些map很慢

某些reduce很快,某些reduce奇慢


如下情况:

1,数据在节点上分布不均匀,这个无法避免,因为整个系统是需要不断扩容的,系统会为了存储均衡,把数据弄到新机器上,但是他并不会说某个表的数据分十分之一上去,另外一个表又分十分之一上去,它是基于块的,所以某个表的执行集中在某几个节点上,这种情况是无法避免的。

2,如果join时on关键词中个别值量很大,如null值,或者说某个相同的值,原因就是每个key对应的map,它只能交到一个reduce来处理,如果这个key数据量非常大,它就只能有一个reduce来处理,而其他key的数据量很少,就会造成有的key运算快,有些key运算慢。

3,count(distinct)在数据量非常大的情况下,也很容易造成数据倾斜,因为count(distinct)是按group by字段分组,按distinct字段排序,其实道理一样,因为group by的时候,你group by的字段也是key

其中1无法避免,2是我们经常用的,可以优化避免数据倾斜,3语法上有时无法避免


join mapjoin groupby是语法优化的重点。