Hive 性能调优

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_43215250/article/details/90719303
  1. Join 优化

    1. 大表放右边,小表放左边。

    2. 多个表关联时,最好分拆成小段,避免大sql

    3. 大表 join 大表可采用 空 key 过滤、空key转换随机赋值

    4. 使用 mapjoin

      开启 mapjoin 设置:

      1. 设置自动选择Mapjoin,默认为true

        set hive.auto.convert.join = true; 
        
      2. 大表小表的阈值设置(默认25M以下认为是小表):

        set hive.mapjoin.smalltable.filesize=25123456; 
        
  2. 控制 Reducer 数量

    Reduce 的最大值可以设为 0.95 * n(n 为 NodeManager 数量)。

    决定Hive 作业的 Reducer 个数

    有下面几个参数

    # Hive 0.14.0 以后默认为 256MB,以前默认为 1G
    hive.exec.reducers.bytes.per.reducer = 256000000
    # Hive 0.14.0 以后默认为 1009 ,以前默认为 999
    hive.exec.reducers.max = 1009
    # 如果 mapred.job.tracke r的值是 local 此参数将会被忽略。在Hadoop中此参数的默认值是1,而在Hive中默认值是-1。通过将此参数设置为-1,Hive将自动计算出应该启动多少个Reduce。
    mapred.reduce.tasks/mapreduce.job.reduces = -1
    

    Reducer 的个数的计算公式

    Reducer 的个数 = min( 参数2, 总输入数据量 / 参数1)  
    

    说明: input size/hive.exec.reducers.bytes.per.reducer > hive.exec.reducers.max,那么Hive启动的Reduce 个数为 hive.exec.reducers.max ;反之为 input size/hive.exec.reducers.bytes.per.reducer 。这个参数只有在mapred.reduce.tasks / mapreduce.job.reduces 设置为负数的时候才有效。

    注意:

    1. Reduce 的个数对整个作业的运行性能有很大影响。
      如果 Reduce 设置的过大,那么将会产生很多小文件,对 NameNode 会产生一定的影响,而且整个作业的运行时间未必会减少,过多的启动和初始化 reduce 也会消耗时间和资源;
      如果Reduce设置的过小,那么单个Reduce处理的数据将会加大,很可能会引起 OOM 异常。

    2. 如果设置了 mapred.reduce.tasks/mapreduce.job.reduces 参数,那么Hive会直接使用它的值作为 Reduce 的个数;

    3. 如果 mapred.reduce.tasks/mapreduce.job.reduces 的值没有设置(也就是-1),那么Hive会根据输入文件的大小估算出 Reduce 的个数。
      根据输入文件估算 Reduce 的个数可能未必很准确,因为 Reduce 的输入是Map的输出,而 Map 的输出可能会比输入要小,所以最准确的数根据 Map 的输出估算 Reduce 的个数。

    设置reduce个数原则

    需要考虑这两个原则

    1. 处理大数据量使用合适的reduce数;
    2. 使单个 reduce 任务处理数据量大小合适。
    
  3. 开启列裁剪

    开启 列裁剪可以减小读取开销。

    # 默认为 true (开启)	
    set hive.optimize.cp = true
    

    例:
    表 test 有 a,b,c,d,e 列,开启列裁剪后,执行如下 HQL 语句

    SELECT a,b from test where a < 10;
    

    Hive 不会读取 c,d,e 列的数据,减小读取开销

  4. 分区裁剪

    开启分区裁剪可以在查询过程中减少不必要的分区,从而减少数据的读入。

    # 默认为 true (开启)	
    set hive.optimize.pruner = true
    

    例:

      SELECT * FROM (SELECT c1, count() FROM T GROUP BY c1) subq WHERE subq.prtn = 100; 	
      SELECT * FROM T1 JOIN (SELECT * FROM T2) subq ON (T1.c1 = subq.c2) subq.prtn = 100;
    

    上述语句会在子查询中就考虑 subq.prtn = 100 条件,从而减少读入的分区数目。

  5. Group By 优化

    1. 开启 Map 端聚合

      启用 map 端部分聚合,其思想和combine类似,这样可机减少要排序并分发到 reducer 的数据。

      # 默认为 true
      hive.map.aggr = true
      # Map 端进行聚合操作的条目数目
      hive.groupby.mapaggr.checkinterval = 10000
      
  6. 合并小文件

    文件数目过多,会给 HDFS 带来压力,可以通过合并 Map 和 Reduce 的输出文件来减少文件数。

    # 是否合并 Map 阶段的输出文件,默认为 true
    hive.merge.mapfiles = true
    # 是否合并 Reduce 阶段的输出文件,默认为 false
    hive.merge.mapredfiles = true
    # 合并后所需的文件大小,默认 256 MB
    hive.merge.size.per.task = 256 000 000
    
  7. Multi-Group By 和 Multi-Insert

    Hive 的 Multi-Group By 和 Multi-Insert 特有的语法可以在同意个查询语句中使用多个不想交的
    Insert 语句,这样分开使用多个 Inert 语句效率高,因为只需要扫描一遍全表。

    FROM test
    INSERT overwrite TABLE test_by_a
    SELECT a, count(e) GROUP BY a
    INSERT overwrite TABLE test_by_b
    SELECT b, count(f) GROUP BY b
    INSERT overwrite TABLE test_by_c
    SELECT c, count(g) GROUP BY c ;	
    

    使用上述特性需要将 FROM 子句放在最前面。

  8. 利用 UNION ALL 特性

    利用 UNION ALL 特性将多个 MapReduce 作业合并。

    SELECT * FROM
      (
       SELECT * FROM t1 GROUP BY c1,c2,c3
       UNION ALL 
       SELECT * FROM t2 GROUP BY c1,c2,c3
      ) t3 GROUP BY c1,c2,c3 ;
    

    上述 HQL 包含3个MapReduce 作业,转化为下面的 HQL 只进行一个 MapReduce作业。

    SELECT * FROM
      (
       SELECT * FROM t1
       UNION ALL 
       SELECT * FROM t2 
      ) t3 GROUP BY c1,c2,c3 ;
    
  9. 并行执行

    Hive 默认是不考虑并行性的,会依次执行作业。可以通过数 hive.exec.parallel 开启并行执行模式。

    # true 为开启,默认为 false 不开启
    hive.exec.parallel = true
    

    一个 HQL 会被 Hive 拆分成多个 MapReduce 作业,这些作业之间有时会相互依赖,有时会相互独立。
    如表1和表2 join的结果与表3和表4 join的结果在join ,那么表1和表2 join 与表3和表4 join这两个作业相互独立,而最后一次 join 作业则依赖于前两个作业,从逻辑上来看,第一个作业和第二个作业可以同时进行。

  10. 全排序

    当执行 ORDER BY 操作时,所有的数据将会集中到 Reducer 里面做一次全排序,当执行 SORT BY 时,则是进行局部排序。可以使用 TotalOrderPartitioner 将全排序变成局部排序。

    首先,指定使用的 Partitioner 和分发区间文件。

    set hive.mapred.partitioner = org.apache.hadoop.mapred.lib.TotalOrderPartitioner;
    set total.order.partitioner.path = /tmp/range_key;
    

    /tmp/range_key 文件是一个 SequeceFile 格式文件,它指定了数据的分发区间。
    生成分发文件的区间可以通过分桶抽样的形式生成,如下:

    # 生成/tmp/range_key 文件
    CREATE EXTERNAL TABLE range_keys(id int) 
    ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.binarysortable.BinarySortableSerDe' 
    STORED AS 
    INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' 
    OUTPutFORMAT 'org.apache.hadoop.hive.ql.io.HiveNullValue SequeceFileOutputFormat' 
    LOCATION '/tmp/range_key';
    
    INSERT OVERWRITE TABLE range_keys
    SELECT DISTINCT id
    FROM SOURCE t_sale sampletable (Bucket 100 OUT 0f 100 ON rand())s
    SORT BY id;
    
  11. Top N

    使用 SORT BY ... LIMIT N 方式,求取 Top N。

    如果使用ORDER BY ... LIMIT N 的话,该 HQL 只会生成一个作业,所有的数据将会集中到一个 Reduce 中进行全排序,这样效率非常低。
    如果使用 SORT BY ... LIMIT N 的方式,Hive 会生成两个作业,在第一个作业中按照 SORT BY 的排序方式,在第二个作业的 Reduce中,将接收到 M * N 条数据进行排序再取 Top N。

猜你喜欢

转载自blog.csdn.net/weixin_43215250/article/details/90719303