hive子查询sql效率优化

今天写了个sql跑了好久,老大过来一看指点下,多用子查询,多用where,然后在join,
提高十多倍,神奇了。想了想,还是老大经验丰富。

如果先join,就全表扫描,然后最后where最后筛选,比较耗时。
如果用子查询,就可以利用where过滤不相关的字段,不但增加了map 数量,还减少了数据量。
以下是我的sql 对比:

SELECT from_unixtime(cast(a.time_calc_rate_end/1000 AS BIGINT),'yyyy-MM-dd') AS p_date,
       sum(CASE
               WHEN (a.time_calc_rate_end -a.time_calc_rate_start+1)/86400000 < f.lock_in_period_after_invest THEN b.principal
               ELSE 0
           END) AS in_advance,
       sum(CASE
               WHEN (a.time_calc_rate_end -a.time_calc_rate_start+1)/86400000 = f.lock_in_period_after_invest THEN b.principal
               ELSE 0
           END) AS
NORMAL
FROM ods_yqg.fdm_yqg_product_to_user a
LEFT JOIN ods_yqg.fdm_yqg_user_asset AS b ON b.dt = "${p_date}"
AND a.product_id = b.product_id
AND a.user_id = b.user_id
LEFT JOIN ods_yqg.fdm_yqg_product_fixed_lock_in_period_after_invest AS f ON a.product_id = f.product_id
AND f.dt = "${p_date}"
WHERE a.dt = "${p_date}"
  AND from_unixtime(cast(time_calc_rate_end/1000 AS BIGINT),'yyyyMMdd') = "${p_date}"
GROUP BY from_unixtime(cast(a.time_calc_rate_end/1000 AS BIGINT),'yyyy-MM-dd')

优化后:

SELECT from_unixtime(cast(a.time_calc_rate_end/1000 AS BIGINT),'yyyy-MM-dd') AS p_date,
       sum(CASE
               WHEN (a.time_calc_rate_end -a.time_calc_rate_start+1)/86400000 < f.lock_in_period_after_invest THEN b.principal
               ELSE 0
           END) AS in_advance,
       sum(CASE
               WHEN (a.time_calc_rate_end -a.time_calc_rate_start+1)/86400000 = f.lock_in_period_after_invest THEN b.principal
               ELSE 0
           END) AS
NORMAL
FROM
  (SELECT product_id,
          user_id,
          time_calc_rate_end,
          time_calc_rate_start
   FROM ods_yqg.fdm_yqg_product_to_user
   WHERE dt = "${p_date}"
     AND from_unixtime(cast(time_calc_rate_end/1000 AS BIGINT),'yyyyMMdd') = "${p_date}") a
LEFT JOIN
  (SELECT product_id,
          principal,
          user_id
   FROM ods_yqg.fdm_yqg_user_asset
   WHERE dt = "${p_date}") b ON a.product_id = b.product_id
AND a.user_id = b.user_id
LEFT JOIN
  (SELECT product_id,
          lock_in_period_after_invest
   FROM ods_yqg.fdm_yqg_product_fixed_lock_in_period_after_invest
   WHERE dt = "${p_date}" ) AS f ON a.product_id = f.product_id
GROUP BY from_unixtime(cast(a.time_calc_rate_end/1000 AS BIGINT),'yyyy-MM-dd');

hive的查询注意事项以及优化总结 :

  • 1:尽量尽早地过滤数据,减少每个阶段的数据量,对于分区表要加分区,同时只选择需要使用到的字段
select ... from A

join B

on A.key = B.key

where A.userid>10

     and B.userid<10

        and A.dt='20120417'

        and B.dt='20120417';

写成:

select .... from (select .... from A

                  where dt='201200417'

                                    and userid>10

                              ) a

join ( select .... from B

       where dt='201200417'

                     and userid < 10   

     ) b

on a.key = b.key;
  • 2、对历史库的计算经验 (这项是说根据不同的使用目的优化使用方法)

    历史库计算和使用,分区

  • 3:尽量原子化操作,尽量避免一个SQL包含复杂逻辑

可以使用中间表来完成复杂的逻辑

  • 4 join操作 小表要注意放在join的左边(有的公司很多都小表放在join的右边)否则会引起磁盘和内存的大量消耗
  • 5:如果union all的部分个数大于2,或者每个union部分数据量大,应该拆成多个insert into 语句,实际测试过程中,执行时间能提升50%
insert overwite table tablename partition (dt= ....)

select ..... from (

                   select ... from A

                   union all

                   select ... from B

                   union all

                   select ... from C

                               ) R

where ...;

可以改写为:

insert into table tablename partition (dt= ....)

select .... from A

WHERE ...;



insert into table tablename partition (dt= ....)

select .... from B

WHERE ...;



insert into table tablename partition (dt= ....)

select .... from C

WHERE ...; 
5:写SQL要先了解数据本身的特点,如果有join ,group操作的话,要注意是否会有数据倾斜
关于数据倾斜的问题请到另一篇博客上

如果出现数据倾斜,应当做如下处理:

set hive.exec.reducers.max=200;

set mapred.reduce.tasks= 200;---增大Reduce个数

set hive.groupby.mapaggr.checkinterval=100000 ;--这个是group的键对应的记录条数超过这个值则会进行分拆,值根据具体数据量设置

set hive.groupby.skewindata=true; --如果是group by过程出现倾斜 应该设置为true

set hive.skewjoin.key=100000; --这个是join的键对应的记录条数超过这个值则会进行分拆,值根据具体数据量设置

set hive.optimize.skewjoin=true;--如果是join 过程出现倾斜 应该设置为true

(1) 启动一次job尽可能的多做事情,一个job能完成的事情,不要两个job来做

通常来说前面的任务启动可以稍带一起做的事情就一起做了,以便后续的多个任务重用,与此紧密相连的是模型设计,好的模型特别重要.

(2) 合理设置reduce个数

reduce个数过少没有真正发挥hadoop并行计算的威力,但reduce个数过多,会造成大量小文件问题,数据量、资源情况只有自己最清楚,找到个折衷点,

(3) 使用hive.exec.parallel参数控制在同一个sql中的不同的job是否可以同时运行,提高作业的并发


2、让服务器尽量少做事情,走最优的路径,以资源消耗最少为目标

比如:

  • 注意join的使用
    若其中有一个表很小使用map join,否则使用普通的reduce join,注意hive会将join前面的表数据装载内存,所以较小的一个表在较大的表之前,减少内存资源的消耗

  • 注意小文件的问题

    在hive里有两种比较常见的处理办法

    第一是使用Combinefileinputformat,将多个小文件打包作为一个整体的inputsplit,减少map任务数

    set mapred.max.split.size=256000000;

    set mapred.min.split.size.per.node=256000000

    set Mapred.min.split.size.per.rack=256000000

    set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat

    第二是设置hive参数,将额外启动一个MR Job打包小文件

    hive.merge.mapredfiles = false 是否合并 Reduce 输出文件,默认为 False

    hive.merge.size.per.task = 256*1000*1000 合并文件的大小

    • 注意数据倾斜

    在hive里比较常用的处理办法

    第一通过hive.groupby.skewindata=true控制生成两个MR Job,第一个MR Job Map的输出 结果随机分配到reduce做次预汇总,减少某些key值条数过多某些key条数过小造成的数据倾斜问题

    第二通过hive.map.aggr = true(默认为true)在Map端做combiner,假如map各条数据基本上不一样, 聚合没什么意义,做combiner反而画蛇添足,hive里也考虑的比较周到通过参数hive.groupby.mapaggr.checkinterval = 100000 (默认)hive.map.aggr.hash.min.reduction=0.5(默认),预先取100000条数据聚合,如果聚合后的条数/100000>0.5,则不再聚合

    • 善用multi insert,union all

    multi insert适合基于同一个源表按照不同逻辑不同粒度处理插入不同表的场景,做到只需要扫描源表一次,job个数不变,减少源表扫描次数

    union all用好,可减少表的扫描次数,减少job的个数,通常预先按不同逻辑不同条件生成的查询union all后,再统一group by计算,不同表的union all相当于multiple inputs,同一个表的union all,相当map一次输出多条

    • 参数设置的调优

    集群参数种类繁多,举个例子比如

    可针对特定job设置特定参数,比如jvm重用,reduce copy线程数量设置(适合map较快,输出量较大)

    如果任务数多且小,比如在一分钟之内完成,减少task数量以减少任务初始化的消耗。可以通过配置JVM重用选项减少task的消耗


关于sql优化的方面,这里有篇不错的文章,可以参考下
https://www.cnblogs.com/xd502djj/p/3799432.html

  • 11
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值