Hive优化
Hive的底层是MapReduce,当数据量太大时,往往可以通过并行来提高效率,比如通过Partition实现运行多个Reduce,可是如果处理不当则容易引发数据倾斜,从而导致效率降低,这就涉及Hive的优化。
MapReduce优化
前面说过HQL解析之后会成为MapReduce程序,所以可以从MapReduce运行的角度来考虑性能优化。
首先考虑的是要避免数据倾斜,比如,通过任务监控页面发现只有少量Reduce任务未完成,则可能出现了数据倾斜问题。在处理大批量数据时,可以通过Partition分区将数据分发到不同的Reduce中进行处理,可是如果分配不当,则可能造成某个Reduce中处理千万条数据,有的Reduce中只处理几十条数据,这就造成了数据倾斜。
另外,尽量避免大量的Job,因为Job数较多的作业往往运行效率比较低。比如有几张表,每个表中的数据有几百到几千条,如果通过多次关联,则可能会产生十几个甚至几十个Job,而Job的初始化等操作都是比较耗资源的,在这种情况下就会造成性能低下。
在Hive中使用SUM、COUNT、MAX、MIN等UDAF函数时,不需要担心出现数据倾斜问题,因为Hadoop在Map端的汇总合并时已经优化过了,不会出现数据倾斜问题。
另外需要注意的是,在遇到COUNT(DISTINCT)的情况下,如果数据量太大,则会效率低下,在遇到多个COUNT(DISTINCT)的情况下则效率会更低,主要是因为COUNT(DISTINCT)是根据GROUP BY字段来分组的。
配置优化
除了使用MapReduce优化,还可以通过Hive相关配置来进行优化,主要分为以下几个方面。
- 列裁剪 列裁剪的意思是忽略不需要的列,Hive在读取数据时,可以仅仅读取需要用到的列,而不需要把所有的列都读取出来。比如有一张表,包括a、b、c、d、e共5列,Hive在查询时仅仅取出需要的b、d、e,而不需要将所有的列都取出来。
- 分区裁剪 另外一个参数优化的方式是在查询过程中减少不必要的分区,分区参数为:hive.optimize.pruner=true(默认值为真)。
- join操作 在写join操作的代码时,最好将数目少的表或者子查询放在join操作符的左边,主要原因是在Reduce阶段,处在join左边的表内容会被加载进内存中。同时,对于同一个key来说,对应的value值小的放在前面,value值大的放在后面,这也是“小表放前”原则。
GROUP BY操作
在进行GROUP BY操作的时候,需要注意以下几方面:
- Map端部分聚合。 由于并不是所有的聚合操作都必须要在Reduce端进行,很多聚合操作可以先在Map端进行聚合,接着在Reduce端得出最终结果。这时需要修改的参数是hive.map.aggr=true,这个参数是用于设定是否需要在Map端进行聚合。另外一个参数是hive.groupby.mapaggr.checkinterval,用于设定Map端进行聚合操作的条目数。
- 在有数据倾斜时进行负载均衡。 与之相关的参数是hive.groupby.skewindata,当设置为true时,生成的查询计划会有两个MapReduce任务。其中,在第一个MapReduce中,Map的输出结果会被随机分布到Reduce中,接着每个Reduce做部分聚合操作,并输出结果。
Limit 限制调整
一般情况下,limit语句还是需要执行整个查询语句,然后再返回部分结果。
有一个配置属性可以开启,避免这种情况: 对数据源进行抽样 。
hive.limit.optimize.enable=true -- 开启对数据源进行采样的功能
hive.limit.row.max.size -- 设置最小的采样容量
hive.limit.optimize.limit.file -- 设置最大的采样样本数
缺点 :有可能部分数据永远不会被处理到
JVM优化
JVM重用是Hadoop调优参数的内容,其对Hive的性能具有非常大的影响,特别是对于很难避免小文件的场景或task特别多的场景,这类场景大多数执行时间都很短。
Hadoop的默认配置通常是使用派生JVM来执行map和Reduce任务的。这时JVM的启动过程可能会造成相当大的开销,尤其是执行的job包含有成百上千task任务的情况。JVM重用可以使得JVM实例在同一个job中重新使用N次。N的值可以在Hadoop的mapred- site.xml文件中进行配置。通常在10-20之间,具体多少需要根据具体业务场景测试得出。
<property>
<name>mapreduce.job.jvm.numtasks</name>
<value>10</value>
<description>How many tasks to run per jvm. If set to -1, there is
no limit.
</description>
</property>
我们也可以在Hive中设置:
set mapred.job.reuse.jvm.num.tasks=10 设置jvm重用
这个功能的缺点是,开启JVM重用将一直占用使用到的task插槽,以便进行重用,直到任务完成后才能释放。如果某个“不平衡的”job中有某几个reduce task执行的时间要比其他Reduce task消耗的时间多的多的话,那么保留的插槽就会一直空闲着却无法被其他的job使用,直到所有的task都结束了才会释放。
数据倾斜优化
数据倾斜的原理都知道,就是某一个或几个key占据了整个数据的90%,这样整个任务的效率都会被这个key的处理拖慢,同时也可能会因为相同的key会聚合到一起造成内存溢出。
Hive的数据倾斜一般的处理方案:
常见的做法,通过参数调优:
set hive.map.aggr=true;
set hive.groupby.skewindata = ture;
当选项设定为true时,生成的查询计划有两个MapReduce任务。
在第一个MapReduce中,map的输出结果集合会随机分布到reduce中,每个reduce做部分聚合操作,并输出结果。
这样处理的结果是,相同的Group By Key有可能分发到不同的reduce中,从而达到负载均衡的目的;
第二个MapReduce任务再根据预处理的数据结果按照Group By Key分布到reduce中(这个过程可以保证相同的Group By Key分布到同一个reduce中),最后完成最终的聚合操作。
但是这个处理方案对于我们来说是个黑盒,无法把控。
那么在日常需求的情况下如何处理这种数据倾斜的情况呢:
- sample采样,获取哪些集中的key;
- 将集中的key按照一定规则添加随机数;
- 进行join,由于打散了,所以数据倾斜避免了; 在处理结果中对之前的添加的随机数进行切分,变成原始的数据。
其他参数调优
开启CLI提示符前打印出当前所在的数据库名
set hive.cli.print.current.db=true;
让CLI打印出字段名称
hive.cli.print.header=true;
设置任务名称,方便查找监控
set mapred.job.name=P_DWA_D_IA_S_USER_PROD;
决定是否可以在 Map 端进行聚合操作
set hive.map.aggr=true;
有数据倾斜的时候进行负载均衡
set hive.groupby.skewindata=true;
对于简单的不需要聚合的类似SELECT col from table LIMIT n语句,不需要起MapReduce job,直接通过Fetch task获取数据
set hive.fetch.task.conversion=more;
## Hive提供的执行计划目前可以查看的信息有以下几种 :
- 查看执行计划的基本信息,即explain;
- 查看执行计划的扩展信息,即explain extended;
- 查看SQL数据输入依赖的信息,即explain dependency;
- 查看SQL操作相关权限的信息,即explain authorization;
- 查看SQL的向量化描述信息,即explain vectorization。
在查询语句的SQL前面加上关键字explain是查看执行计划的基本方法。用explain打开的执行计划包含以下两部分:
- 作业的依赖关系图,即STAGE DEPENDENCIES;
- 每个作业的详细信息,即STAGE PLANS。