深入学习《Programing Hive》:Tuning

        HiveQL是一种声明式语言,最终会被编译为MapReduce job提交到Hadoop执行。大多情况下,用户并不需要知道Hive是如何运作——只关注手头的业务处理问题就行了。虽然Hive引擎会在在HiveQL语句编译过程中最许多的复杂的工作——查询解析、规划、优化和执行等复杂过程,但是用户大部分时间都可以无视这些过程。
        然而,随着Hive的使用,用户会变得越来越有经验,这时候再深入了解和学习Hive理论后的底层实现细节,用户写的HiveQL语句会更高效,特别是在改写HiveQL时做过更多的性能调优之后。
        本节我们会学习一些性能调优的手段。
       
        优化Jion连接
        Streamtable
        在做在对多个表Jion连接操作时,将小表放在Jion的左边,大表放在Jion的右边,在执行这样的Jion连接时小表中的数据会被缓存到内存当中,这样可以有效减少发生内存溢出错误的几率:
             hive> SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1) WHERE c.val >= b.val AND a.val = 'test' AND b.val = 20130520
        

        对于上面的HiveQL示例,会先执行ON条件筛选,再连接,然会才会执行WHERE过滤条件,ON后的筛选条件主要是针对从表,对主表不起作用,因为是外关联,主表数据都会输出,对于主表的筛选条件应该放在where后面,如果觉得主表不需要关联的数据太多,可以使用子查询,先过滤主表中无用数据
              Hive> SELECT a.val,b.val FROM a JION (
                      SELECT c.val FROM c WHERE c.key = 'test'
                  > ) b ON(a.key = b.key1)
                  > WHERE b.val = 20130520;
        

        由于join操作是在where操作之前执行,所以当你在执行join时,where条件并不能起到减少join数据的作用,如:
              hive> SELECT a.val, b.val FROM a LEFT OUTER JOIN b ON (a.key=b.key)
                  >WHERE a.ds='2009-07-07' AND b.ds='2009-07-07'
        
可以改为:
              hive> SELECT a.val, b.val FROM a LEFT OUTER JOIN b
                  > ON (a.key=b.key AND b.ds='2009-07-07' AND a.ds='2009-07-07')
        

        在JION操作的每一个MapReduce程序中,Hive都会把出现在JION语句中相对靠后的表的数据stream化,相对靠前的变的数据缓存在内存中,这种情况下最好将大表放在JION的右边
(表中数据a < b < c):
              hive> SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1);
        

        当然您也可以不将大表放在JION的右边,这是就需要指定使用/*+ STREAMTABLE(..) */:
              hive> SELECT /*+ STREAMTABLE(b) */ a.val, b.val, c.val FROM a JOIN b
                  > ON (a.key = b.key1) JOIN c ON (c.key = b.key1)
        

        另外如果所有参与join的表中其参与join的key都相同,则会将所有的join合并到一个mapred程序中。
       

        Mapjion
        Mapjion是是另外一种特殊JION连接操作,它强调在Map端做JION操作:
              hive> SELECT /*+ MAPJION(a,b) */ a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1) WHERE c.val >= b.val AND a.val = 'test' AND b.val = 20130520
        

        默认情况下Hive是不会在MapJION操作的,用户需要使用 /* + MAPJION(tablelist) */关键字来告知Hive编译器来做Map端的连接。 MAPJION会把小表全部读入内存中,在map阶段直接拿另外一个表的数据和内存中表数据做匹配,由于在map是进行了join操作,省去了reduce运行的效率也会高很多。
        Mapjion是一种避免避免数据倾斜的手段,比如对a表和b表做jion连接操作,其中a中只有很少的上千行或百十行数据,而b中却又上千万或上亿行的数据,而且B表中数据倾斜特别严重,这时候制定对a表示用Mapjion,这样就不会由于数据倾斜导致某个reduce上落数据太多而失败。
        不同的JION连接操作是不支持不等于操作的(如a.x < b.y 或者 a.x like b.y等),Hive语法解析会直接抛出错误。 Mapjoin还有一个很大的好处是能够进行不等连接的join操作,如果将不等条件写在WHERE中,那么MapReduce过程中会进行笛卡尔积,运行效率特低,若使Mapjoin操作,在Mapper处理过程中就可以完成了不等值的join操作,效率会高很多:
             hive> select /*+ MAPJION(a) */ a.a ,a.b from a join b where a.a>b.a;
       

       Mapjoin 的限制是无法执行全连接(FULL JION)和右外连接(RIGHT OUTER JOIN)。

       并行执行(Parallel Execution)
       Hive引擎会将一个HiveQL查询语句分解成几个执行阶段(stages)(可以通过EXPLAIN HiveQL看出),默认情况下,Hive会一次执行一个阶段(stage),有时候一个包含多个执行阶段(stages)的HiveQL的stages之间并没有前后执行的依赖顺序,这种情况下将这些stages并行执行是一个很好的选择,可以带来更好的性能。
       将 hive.exec.parallel参数设置为true就会激活并行执行的功能,如果一个HiveQL作业可以并行的执行其多个stages,这就会提升集群的利用率:
 
             <property>
                 <name>hive.exec.parallel</name>
                 <value>true</value>
                 <description>Whether to execute jobs in parallel</description>
             </property>
       


       本地模式(Local Mode)
       在处理大的数据集时,Hadoop作业可以从hadoop的全扩展可以带来很大的优势,然而有时候要处理的数据集会很小,在这种场景中,对小数据集的查询激活本机模式是一个不错的选择,这可以相当程度上的避免一些不必要的性能开销。可以在$HIVE_HOME/conf/hive.site.xml中将 hive.exec.mode.local.auto参数设置为true由Hive对HiveQL作业自动设置执行模式:
             <property>
                 <name>hive.exec.mode.local.auto</name>
                 <value>true</value>
                 <description>
                     Let hive determine whether to run in local mode automatically
                 </description>
             </property>
       


       JVM重用(JVM Reuse)
       JVM重用是Hadoop的一个性能调整的参数,它也会对Hive产生非常大的影响,特别是在很那避免处理大量小文件的场景中,每一个task执行方式景都很短,要知道默认新情况下每处理一个task都要启动一个JVM进程,JVM进程是一个重量级的进程,这就需要大量的资源的开销,这种情况下使用JVM重用可以大量减少这些不必要的JVM的初始化和销毁的开销,大大缩短总的执行时间。
       这个参数可以再$HADOOP_HOME/conf/mapred-site.xml中设置:
             <property>
                 <name>mapred.job.reuse.jvm.num.tasks</name>
                 <value>10</value>
                 <description>
                     How many tasks to run per jvm.if set to -1,there is no limit.
                 </description>
             </property>
       

       JVM重用必须是针对一个MapReduce作业的同一种task任务的,同一个作业的不同类的task是不能重用JVM的。mapred.job.reuse.jvm.num.tasks为-1表明不限制JVM重用的次数。

       推测式执行(Speculative Execution)
       推测式执行时Hadoop的一个重要的功能,其目的是大大加快hadoop作业的整体执行时间。推测式执行的细节这里不再赘述。这个功能需要设置mapred-site.xml中的两个参数来激活这一功能:
             <property>
                 <name>mapred.map.tasks.speculative.execution</name>
                 <value>true</value>
                 <description>If true,the multiple instances of some map tasks 
                   may be executed in parallel.
                 </description>
             </property>
             <property>
                 <name>mapred.reduce.tasks.speculative.execution</name>
                 <value>true</value>
                 <description>If true,the multiple instances of some reduce tasks 
                   may be executed in parallel.
                 </description>
             </property>
       

       Hive提供了一个参数来控制reduce端的并行执行:
             <property>
                 <name>hive.mapred.reduce.tasks.speculative.execution</name>
                 <value>true</value>
                 <description>
                     Whether speculative execution for reducers should be turned on.
                 </description>
             </property>
       


       索引(Indexes)
       索引可以加快GROUP BY查询语句的执行速度。
       Hive从0.80开始,提供了一个Bitmap位图索引,它主要适用于在一个给定的列中只有几个值的场景。详情见 HiveQL索引

       严格模式(Strict Mode)
       Hive中的严格模式可以防止用户发出(可以有问题)的查询无意中造成不良的影响。
       将hive.mapred.mode设置成strict可以禁止三种类型的查询:
       1)、在一个分区表上,如果没有在WHERE条件中指明具体的分区,那么这是不允许的,换句话说,不允许在分区表上全表扫描。这种限制的原因是分区表通常会持非常大的数据集并且可能数据增长迅速,对这样的一个大表做全表扫描会消耗大量资源,结果示例如下:
             hive> SELECT DISTINCT(planner_id) FROM fracture_ins WHERE planner_id = 5;
             FAILED: Error in semantic analysis: No Partition Predicate Found For
             Alias "francture_ins" Table "francture_ins"
       

       必要再WHERE过滤条件中具体指明分区才可以执行成功的查询:
             hive> SELECT DISTINCT(planner_id) FROM fracture_ins 
                 > WHERE planner_id = 5 AND hit_date=20121212;
             ... 结果信息 ...
       

       2)、第二种是禁止执行有ORDER BY的排序要求但没有LIMIT语句的HiveQL查询。因为ORDER BY全局查询会导致有一个单一的reducer对所有的查询结果排序,如果对大数据集做排序,这将导致不可预期的执行时间:
             hive> SELECT * FROM fracture_ins WHERE hit_date>2012 ORDER BY planner_id;
             FAILED: Error in semantic analysis: line 1:56 In strict mode,
             limit must be specified if ORDER BY is present planner_id
       

       应该给上面的HiveQL语句加上LIMIT限制:
             hive> SELECT * FROM fracture_ins WHERE hit_date>2012 ORDER BY planner_id LITMIT 1000;
       

       3)、第三种是禁止产生笛卡尔集。在JION接连查询中没有ON连接key而通过WHERE条件语句会产生笛卡尔集:
             hive> SELECT * FROM fracture_act JION fracture_ads
                 > WHERE fracture_act.planner_id = fracture_ads.planner_id;
             FAILED: Error semantic analysis: In strict mode,cartesian product is
             not allowed.If you really want to perform the operation, 
             +set hive.mapred.mode=nonstrict+ 
       

       将之改为JION...ON语句:
             hive> SELECT * FROM fracture_act JION fracture_ads
                 > ON(fracture_act.planner_id = fracture_ads.planner_id);
             ... 查询结果 ...
       

猜你喜欢

转载自flyingdutchman.iteye.com/blog/1871983