一个Hive查询生成多个map reduce job,一个map reduce job又有map,reduce,spill,shuffle,sort等多个阶段,因此针对hive查询的优化能够大体分为针对MR中单个步骤的优化(其中又会有细分),针对MR全局的优化,和针对整个查询(多MR job)的优化,下文会分别阐述。web
简单查询的Fetch Task功能;sql
在Hive中执行一个简答的查询功能(select * 除外),都会转换为mapreduce做业,提交到hadoop集群上执行,这样比较消耗时间,因此从Hive 0.10.0版本开始,Hive支持Fetch Task功能,开启了这个功能后,咱们执行简单的查询语句(没有函数,排序等的查询语句)时,不会生成mapreduce做业,而直接使用Fetch Task从HDFS文件中查询出数据,这样对于简单查询效率会更高。app
配置Fetch Task的方式有如下三种:jvm
-1. set hive.fetch.task.conversion=more; -2. hive --hiveconf hive.fetch.task.conversion=more; -3. 修改hive-site.xml文件; 其中方法1和2,只对当前的会话有效,退出会话后,下次执行简单查询时,默认开启mapreduce. 而方法3会永久有效;
Map阶段的优化,主要是肯定合适的map数。那么首先要了解map数的计算公式:函数
num_map_tasks = max[${mapred.min.split.size}, min(${dfs.block.size}, ${mapred.max.split.size}) ] mapred.min.split.size指的是数据的最小分割单元大小。 mapred.max.split.size指的是数据的最大分割单元大小。 dfs.block.size指的是HDFS设置的数据块大小。
通常来讲dfs.block.size
这个值是一个已经指定好的值,并且这个参数hive是识别不到的:oop
hive> set dfs.block.size; dfs.block.size is undefined
因此实际上只有mapred.min.split.size
和mapred.max.split.size
这两个参数(本节内容后面就以min和max指代这两个参数)来决定map数量。在hive中min的默认值是1B,max的默认值是256MB:性能
hive> set mapred.min.split.size; mapred.min.split.size=1 hive> set mapred.max.split.size; mapred.max.split.size=256000000
因此若是不作修改的话,就是1个map task处理256MB数据,咱们就以调整max为主。经过调整max能够起到调整map数的做用,减少max能够增长map数,增大max能够减小map数。须要提醒的是,直接调整mapred.map.tasks
这个参数是没有效果的。fetch
调整大小的时机根据查询的不一样而不一样,总的来说能够经过观察map task的完成时间来肯定是否须要增长map资源。若是map task的完成时间都是接近1分钟,甚至几分钟了,那么每每增长map数量,使得每一个map task处理的数据量减小,可以让map task更快完成;而若是map task的运行时间已经不多了,好比10-20秒,这个时候增长map不太可能让map task更快完成,反而可能由于map须要的初始化时间反而让job整体速度变慢,这个时候反而须要考虑是否能够把map的数量减小,这样能够节省更多资源给其余Job。优化
Reduce阶段优化的主要工做也是选择合适的reduce task数量,跟上面的map优化相似。
与map优化不一样的是,reduce优化时,能够直接设置mapred.reduce.tasks
参数从而直接指定reduce的个数。固然直接指定reduce个数虽然比较方便,可是不利于自动扩展。Reduce数的设置虽然相较map更灵活,可是也能够像map同样设定一个自动生成规则,这样运行定时job的时候就不用担忧原来设置的固定reduce数会因为数据量的变化而不合适。spa
Hive估算reduce数量的时候,使用的是下面的公式:
num_reduce_tasks = min[${hive.exec.reducers.max}, (${input.size} / ${ hive.exec.reducers.bytes.per.reducer}) ]
hive.exec.reducers.bytes.per.reducer
默认为1G,也就是每一个reduce处理至关于job输入文件中1G大小的对应数据量,并且reduce个数不能超过一个上限参数值,这个参数的默认取值为999。因此咱们也能够用调整这个公式的方式调整reduce数量,在灵活性和定制性上取得一个平衡。
设置reduce数一样也是根据运行时间做为参考调整,而且能够根据特定的业务需求、工做负载类型总结出经验,因此再也不赘述。
map phase和reduce phase之间主要有3道工序。首先要把map输出的结果进行排序后作成中间文件,其次这个中间文件就能分发到各个reduce,最后reduce端在执行reduce phase以前把收集到的排序子文件合并成一个排序文件。须要强调的是,虽然这个部分能够调的参数挺多,可是大部分在通常状况下都是不要调整的,除非能精准的定位到这个部分有问题。
在spill阶段,因为内存不够,数据可能没办法在内存中一次性排序完成,那么就只能把局部排序的文件先保存到磁盘上,这个动做叫spill,而后spill出来的多个文件能够在最后进行merge。若是发生spill,能够经过设置io.sort.mb
来增大mapper输出buffer的大小,避免spill的发生。另外合并时能够经过设置io.sort.factor
来使得一次性可以合并更多的数据,默认值为10,也就是一次归并10个文件。调试参数的时候,一个要看spill的时间成本,一个要看merge的时间成本,还须要注意不要撑爆内存(io.sort.mb
是算在map的内存里面的)。Reduce端的merge也是同样能够用io.sort.factor
。通常状况下这两个参数不多须要调整,除非很明确知道这个地方是瓶颈。好比若是map端的输出太大,考虑到map数不必定能很方便的调整,那么这个时候就要考虑调大io.sort.mb
(不过即便调大也要注意不能超过jvm heap size)。而map端的输出很大,要么是每一个map读入了很大的文件(好比不能split的大gz压缩文件),要么是计算逻辑致使输出膨胀了不少倍,都是比较少见的状况。
这里说的copy,通常叫作shuffle更加常见。可是因为一开始的配图以及MR job的web监控页对这个环节都是叫copy phase,指代更加精确,因此这里称为copy。
copy阶段是把文件从map端copy到reduce端。默认状况下在5%的map完成的状况下reduce就开始启动copy,这个有时候是很浪费资源的,由于reduce一旦启动就被占用,一直等到map所有完成,收集到全部数据才能够进行后面的动做,因此咱们能够等比较多的map完成以后再启动reduce流程,这个比例能够经过mapred.reduce.slowstart.completed.maps
去调整,他的默认值就是5%。若是以为这么作会减慢reduce端copy的进度,能够把copy过程的线程增大。tasktracker.http.threads
能够决定做为server端的map用于提供数据传输服务的线程,mapred.reduce.parallel.copies
能够决定做为client端的reduce同时从map端拉取数据的并行度(一次同时从多少个map拉数据),修改参数的时候这两个注意协调一下,server端能处理client端的请求便可。
另外,在shuffle阶段可能会出现的OOM问题,缘由比较复杂,通常认为是内存分配不合理,GC没法及时释放内存致使。对于这个问题,能够尝试调低shuffle buffer的控制参数mapred.job.shuffle.input.buffer.percent
这个比例值,默认值0.7,即shuffle buffer占到reduce task heap size的70%。另外也能够直接尝试增长reduce数量。
文件格式方面有两个问题,一个是给输入和输出选择合适的文件格式,另外一个则是小文件问题。小文件问题在目前的hive环境下已经获得了比较好的解决,hive的默认配置中就能够在小文件输入时自动把多个文件合并给1个map处理,输出时若是文件很小也会进行一轮单独的合并,因此这里就不专门讨论了。相关的参数能够在这里找到(http://blog.csdn.net/yfkiss/article/details/8590486)。