优化手段:
合理控制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是语法优化的重点。