Hive优化

1. hive group by distinct区别以及性能比较

https://blog.csdn.net/xiaoshunzi111/article/details/68484426

2. 用insert into替换union all

3. order by & sort by 

 

执行计划是什么

执行计划代表HiveSQL会转化成怎么样的MapReduce作业。也是优化HiveSQL根本依据。

HiveSQL的优化本质是对MapReduce作业的优化。不管使用的引擎是mrtez还是spark都是一样的。

 

执行计划初步解析

抽象语法树ASTAbstract Syntax Tree

是源代码的抽象语法结构的树状表现形式

Hive使用Antlr实现SQL的词法和语法解析生成AST

 

QueryBlock

QueryBlock是一条SQL最基本的组成单元,包括三个部分:输入源,计算过程,输出。简单来讲一个QueryBlock就是一个子查询

 

Operator

Hive最终生成的MapReduce任务,Map阶段和Reduce阶段均由OperatorTree组成。逻辑操作符,就是在Map阶段或者Reduce阶段完成单一特定的操作。

基本的操作符包括TableScanOperatorFilterOperatorJoinOperatorGroupByOperatorReduceOutputOperatorFileOutputOperator

OperatorMap Reduce阶段之间的数据传递都是一个流式的过程。每一个Operator对一行数据完成操作后之后将数据传递给childOperator计算

 

逻辑层优化器、物理层优化器

SimpleFetchOptimizerMapJoinProcessorGroupByOptimizerPredicatePushDown等等

 

MapReduce任务生成步骤

AST>>QueryBlock>>Operator Tree>>MapReduce Job

 

查看执行计划:

EXPLAIN [EXTENDED|DEPENDENCY|AUTHORIZATION] query

Explain select
    b.pid,
    count(distinct a.wid)
from default.saas_uc_user_info b
join d_extra.dim_shp_saas_pid_info_ext a
on a.id=b.pid
join default.t_march_merchant_info c
on b.wid=c.wid
group by b.pid

 

数据倾斜

map/reduce程序执行时,reduce节点大部分执行完毕,但是有一个或者几个reduce节点运行很慢,导致整个程序的处理时间很长,这是因为某一个key的条数比其他key多很多(有时是百倍或者千倍之多),这条key所在的reduce节点所处理的数据量比其他节点就大很多,从而导致某几个节点迟迟运行不完,此称之为数据倾斜

特殊情况:map端“数据倾斜”

num_map_tasks = max[${mapred.min.split.size},min(${dfs.block.size},${mapred.max.split.size})]

通过调整max可以起到调整map数的作用,减小max可以增加map数,增大max可以减少map数。需要提醒的是,直接调整mapred.map.tasks这个参数是没有效果的。

数据量大不是问题,数据倾斜是个问题

 

倾斜的原因

使map的输出数据更均匀的分布到reduce中去,是我们的最终目标。由于Hash算法的局限性,按key Hash会或多或少的造成数据倾斜。大量经验表明数据倾斜的原因是人为的建表疏忽或业务逻辑可以规避的

 

解决思路:

Hive的执行是分阶段的,map处理数据量的差异取决于上一个stagereduce输出,所以如何将数据均匀的分配到各个reduce中,就是解决数据倾斜的根本

 

典型的业务场景

NULL值产生的数据倾斜或其他默认业务值。比如常见的空值,0,1,-1,-99等业务默认值。

例:如日志中,常会有信息丢失的问题,比如电商日志store_id如果取其中的 store_id 和 门店store_id 关联,会碰到数据倾斜的问题

解决办法:

1.成两段union all在一起

2.给空值做转换join条件写成

A left join B
On
case when A.store_id= null then concat('hive',rand()) else A.store_id end
= 
B.store_id

园园的例子:

drop table temp.qyy_test_1  ;

create table temp.qyy_test_1 as

select

    logdate , cuid, max(wid) wid

from datacleanup.mdpath

where logdate >= '2017-05-01' and logdate <= '2017-05-20'

group by logdatecuid;

 

drop table temp.qyy_test_2  ;

create table temp.qyy_test_2 as

select

    t1.logdate,

    t1.cuid,

    t2.wid

from temp.qyy_test_1 t1

left join d_extra.dm_user_wid_msg t2 on ( t1.wid = t2.wid  );

drop table temp.qyy_test_3  ;

create table temp.qyy_test_3 as

select

    t1.logdate,

    t1.cuid,

    t2.wid

from temp.qyy_test_1 t1

left join d_extra.dm_user_wid_msg t2

    on ( if (t1.wid > 0,t1.wid, cast(ceiling(rand() * -65535) as bigint)) = t2.wid ) ;

结论:

如果关联不上的KEY数据太多,可使用rand()将这种数据均匀分布到各个reducer中。

 

 

count distinct

set hive.map.aggr=true;

sum,count,max,minUDAF,不怕数据倾斜问题,hadoopmap端的汇总合并优化,使数据倾斜不成问题。

 

此语句非常容易产生数据倾斜,因为其执行的MapReduce是以GroupBy分组,再对distinct列排序,然后输出交给Reduce.

问题就在这里,相比其它GroupBy聚合统计,count(distinct)少一个关键步骤(Map的预计算,Map端提前做一次聚合再将聚合结果交给Reduce)

Map直接将全部数据交给Reduce后,如果数据的分组本身不平衡(存在大量值为NULL或空的记录,比如及格,80%以上及格数据),会造成某一些Reduce处理太过多的数据。

 

解决方式:

如果是仅计算count distinct,可以不用处理,直接过滤,在最后结果中加1;或者拆成count(1) from (select distinct)

如果还有其他计算需要进行group by,可以先将值为空的记录单独处理,再和其他计算结果进行union all

 

set hive.groupby.skewindata=true

 

只能支持一个distinct

生成的查询计划有两个MapReduce 任务

 

eg: 园园的例子

使用Distinct会将所有order_noshuffle到一个reducer中,这就导致了数据倾斜。

Group By会启动457reducer,将数据均匀的分布到多个CPU上执行。 这样速度就会快很多。

 

group by

Select

    wid , count(*)

From datacleanup.mdpath

Where logdate = ‘2017-05-01

Group by wid ;

以上脚本也会产生数据倾斜, 因为wid null or -1的记录很多。

解决办法:

  set hive.groupby.skewindata=true; 

  set hive.groupby.mapaggr.checkinterval=100000 ;

当某个KEY的数据量超过这个值时, hive就会产生一个新的reducer去处理。

 

不同数据类型关联产生数据倾斜

场景:用户表中user_id字段为intlog表中user_id字段既有string数据也有int数据。当按照user_id进行两个表的Join操作时,默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer

解决方法:把数字类型转换成字符串类型

 

Join操作产生数据倾斜

大表和小表Join

产生原因:Hive在进行join时,按照joinkey进行分发,而在join左边的表的数据会首先读入内存,如果左边表的key相对分散,读入内存的数据会比较小,join任务执行会比较快;而如果左边的表key比较集中,而这张表的数据量很大(也容易发生OOM错误),那么数据倾斜就会比较严重,而如果这张表是小表,则还是应该把这张表放在join左边

解决方式:使用mapjoin

Join 操作在 Map 阶段完成,不再需要Reduce,也就不需要经过Shuffle过程,从而能在一定程度上节省资源提高JOIN效率。

可以变相支持不等连接

0.7.0版本之前:需要在sql中使用  /*+ MAPJOIN(smallTable) */ 

例:SELECT /*+ MAPJOIN(b) */

a.key, a.value

FROM a JOIN b

ON a.key = b.key;

 

0.7.0版本之后:可以set hive.auto.convert.join=true;

 

其他相关参数:

set hive.mapjoin.smalltable.filesize=100000000; 小表最大大小

set hive.auto.convert.join.noconditionaltask=true; 合并多个MJ为一个

set hive.auto.convert.join.noconditionaltask.size=894435328; MJ的小表总大小,如总大小小于此值则合并。

 

小技巧

1.取需要的字段

2.先过滤

 

总结:

1.数据量大不是问题,数据倾斜是个问题

2. jobs数比较多的作业运行效率相对比较低,比如即使有几百行的表,如果多次关联多次汇总,产生十几个jobs,耗时很长。原因是map reduce作业初始化的时间是比较长的。(tez做的事情)

3. sum,count,max,minUDAF,不怕数据倾斜问题,hadoopmap端的汇总合并优化,使数据倾斜不成问题

4.数据倾斜是导致效率大幅降低的主要原因,可以采用多一次 Map/Reduce 的方法, 避免倾斜

----Credit to Peiqing

 

 

优化意识及思路

MR流程图

WordCount

 

方法论:

1.收集数据explain查看执行计划

2.定位瓶颈
1)查看执行日志,定位哪个StageJob)时间长
2)查看Job日志,定位Map阶段慢还是Reduce阶段慢

3.诊断问题
1)资源不足pending
2
)数据倾斜
3reduce数太少
4...

4.解决瓶颈:对症下药,蛇打七寸

 

如何优化?

        一个Hive查询生成多个Map Reduce Job,一个Map Reduce Job又有Map,Spill,Shuffle,Sort,Reduce等多个阶段,所以针对Hive查询的优化可以大致分为针对MR中单个步骤的优化(其中又会有细分),针对MR全局的优化,和针对整个查询(多MR Job)的优化。。。

 

Map阶段的优化

Map阶段的优化,主要是确定合适的Map

num_Map_tasks = ${input.size} /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设置的数据块大小。

需要提醒的是,直接调整 mapred.map.tasks 这个参数是没有效果的

 

Reduce阶段的优化

Reduce阶段的优化,主要是确定合适的Reduce

        与Map优化不同的是,Reduce优化时,可以直接设置mapred.reduce.tasks参数从而直接指定Reduce的个数。当然直接指定Reduce个数虽然比较方便,但是不利于自动扩展

num_Reduce_tasks = min[${hive.exec.reducers.max},(${input.size} / ${ hive.exec.reducers.bytes.per.reducer})] 

 

hive.exec.reducers.max --reduce数的上限值

 

MapReduce之间的优化

Spill Sort

io.sort.mb

io.sort.factor

Copy

mapred.reduce.slowstart.completed.maps

 

tasktracker.http.threads

mapred.reduce.parallel.copies

 

文件格式的优化

文件格式

1. 压缩比例

2. 查询时间

Textfile

Rcfile

parquet

Orcfile

小文件合并

https://blog.csdn.net/yfkiss/article/details/8590486

1.Map输入合并小文件

2.输出合并

 

执行模式

本地模式(数据量小时)

分布模式(数据量大时)

 

//开启本地模式

set hive.exec.mode.local.auto = true

//job的最大map

hive.exec.mode.local.auto.tasks.max  

//job的最大输入数据量, 一般 = dfs.block.size

hive.exec.mode.local.auto.inputbytes.max

 

JVM重用

        JVM重用正常情况下,MapReduce启动的JVM在完成一个task之后就退出了,但是如果任务花费时间很短,又要多次启动JVM的情况下(比如对很大数据量进行计数操作),JVM的启动时间就会变成一个比较大的overhead

 

在这种情况下,可以使用jvm重用的参数: mapred.job.reuse.jvm.num.tasks  = 5;  

他的作用是让一个jvm运行多次任务之后再退出。这样一来也能节约不少JVM启动时间。

 

三大经典join算法

Nested join

Hash join

Sort merge join

Join算法

处理分布式join,一般有两种方法:

replication join:把其中一个表复制到所有节点,这样另一个表在每个节点上面的分片就可以跟这个完整的表join了;

repartition join:把两份数据按照join key进行hash重分布,让每个节点处理hash值相同的join key数据,也就是做局部的join。

这两种方式在M/R Job中分别对应了Map side joinReduce side join

 

Map-side join(小表复制的代价会好过大表Shuffle的代价)

MapJoin通常用于一个很小的表和一个大表进行join的场景,具体小表有多小,由参数hive.mapjoin.smalltable.filesize来决定,该参数表示小表的总大小,默认值为25000000字节,即25M

Hive0.7之前,需要使用hint提示 /*+ mapjoin(table) */才会执行MapJoin,否则执行Common Join,但在0.7版本之后,默认自动会转换Map Join,由参数hive.auto.convert.join来控制,默认为true.

法一:hint,语法是/*+MapJOIN (tbl)*/,其中tbl就是你想要做replication的表

法二:

hive.auto.convert.join

hive.mapjoin.smalltable.filesize

Broadcast hash Join

 

bucket Map join(当小表内存里放不下时)

原理:

两个join表在join key上都做hash bucket,这样数据就会按照join key做hash bucket。

小表依然复制到所有节点,Map join的时候,小表的每一组bucket加载成hashtable,与对应的一个大表bucket做局部join,这样每次只需要加载部分hashtable就可以了。

要点:

1.map join一起工作( hive.optimize.bucketmapjoin  = true;

2.所有要join的表都必须做了分桶(bucket) , 大表的桶个数是小表桶个数的整数倍.

3.做了bucket必须=join

4.bucket表里查数据时,必须hive.enforce.bucketing=true

 

Sort merge bucket Map join

两个表的join key都具有唯一性的时候(也就是可做主键),还可以进一步做Sort merge bucket Map join。做法还是两边要做hash bucket,而且每个bucket内部要进行排序。这样一来当两边bucket要做局部join的时候,只需要用类似merge Sort算法中的merge操作一样把两个bucket顺序遍历一遍即可完成,这样甚至都不用把一个bucket完整的加载成hashtable,这对性能的提升会有很大帮助。

在bucket Map join的基础上加上下面的设置即可:

hive.optimize.bucketmapjoin.sortedmerge  = true;  

hive.input.format  = org.apache.Hadoop.Hive.ql.io.BucketizedHiveInputFormat;

 

Join总结

一般Map join的优化效果已经很明显了。

如果小表不能完全放内存,但是小表相对大表的size量级差别也非常大的时候也可以试试bucket Map join,不过其hash table分发的过程会浪费不少时间,需要评估下是否能够比Reduce join更高效。

Sort merge bucket Map join虽然性能不错,但是把数据做成bucket本身也需要时间,另外其发动条件比较特殊,就是两边join key必须都唯一

  

并行

场景:当需要执行多个子查询union all或者join操作的时候

并行执行的确可以大的加快任务的执行速率,但不会减少其占用的资源。

//打开任务并行执行

set hive.exec.parallel=true;  

//同一个sql允许最大并行度,默认为8

set hive.exec.parallel.thread.number=16;

 

数据倾斜

group by造成的倾斜

set hive.map.aggr=true

 

join造成的倾斜

set hive.optimize.skewjoin= true;

set hive.skewjoin.key= 阀值;

原理:

特殊值单独join转化成map join

union all ------------------------------------------------------------

非特殊值转化成没有倾斜的普通join

 

Left semi join

以下2个语句,结果是否一样

 

select a.id

from tmp.aaa a inner join tmp.bbb b

on a.id=b.id;

 

select a.id

from tmp.aaa a left semi join tmp.bbb b

on a.id=b.id;

insert overwrite table tmp.aaa
select '1'
union all
select '2'
union all 
select '3’
; 
insert overwrite table tmp.bbb
select ‘2’
;


---------

insert overwrite table tmp.bbb
select '2'
union all
select '2'
;



CREATE TABLE tmp.aaa (
  `id` string
)
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.ql.io.orc.OrcSerde' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.orc.OrcInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat'

实现IN/EXISTS子查询

SELECT A.*

FROM A WHERE A.KEY IN

(SELECT B.KEY FROM B WHERE B.VALUE > 100);

 

等同于:

SELECT A.*

FROM A LEFT SEMI JOIN B

ON (A.KEY = B.KEY and B.VALUE > 100);

 

Left semi join 总结

1selectwhere语句不能引用到右表里的字段(横向不可扩展字段)

2,不会增加主(左)表的行数(纵向无副作用)

3,一旦匹配, 立即退出

 

sort by

Order by 实现全局排序,一个reduce实现,效率低 
Sort by 实现部分有序,单个reduce输出的结果是有序的,效率高,通常和DISTRIBUTE BY关键字一起使用(DISTRIBUTE BY关键字 可以指定map 到 reduce端的分发key) 
CLUSTER BY col1 等价于DISTRIBUTE BY col1 SORT BY col1 

 

分区消除(裁剪)

以下2句哪个效率高?

 

select a.id ,b.id

from tmp.aaa a inner join tmp.bbb b

on a.id=b.id and a.id=1

 

select a.id ,b.id

from tmp.aaa a inner join tmp.bbb b

on a.id=b.id

where a.id=1

通常是 a.id写在on里效率高,但是如果a.id是一个分区字段呢?

 

等价改写

需求:查询日志中同时访问过页面a和页面b的用户数量

面向明细

select count(*)

from (select wid from logs where pagename = 'a' group by wid) a

join (select wid from logs where pagename = 'b' group by wid) b

on a. wid = b.wid;

2个求子查询的job,一个用于关联的job,还有一个计数的job,一共有4job
 

面向集合

select count(*) from (

  select wid

  from logs group by wid

  having count(case when pagename = 'a' then 1 end) *count(case when pagename = 'b' then 1 end) > 0

) t;
只需要用两个job就能跑完(1个子查询的group by, 1个count)
 

打个比方,你去一个会场里找对象,条件是1.7以上+30岁以下+肤白貌美,面向明细的做法就是先转一圈,挑出1.7以上的,再转一圈,挑出30岁以下的。。。面向集合的做法就是只转一圈,挑出同时满足的所有条件的。

job的启动是有成本的,我们尽可能在启动一个job时,让它多干活。

 

等价改写的陷阱

以下2句的结果是否一样?

select *

from tmp.ccc

where id1=1 or id2=1

 

select *

from tmp.ccc

where id1=1

union all

select *

from tmp.ccc

where id2=1

 

准备数据:

CREATE TABLE tmp.ccc (
  `id1` string,
  `id2` string
)
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.ql.io.orc.OrcSerde' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.orc.OrcInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat'  
;


insert overwrite table tmp.ccc
select '1' ,'0'
union all
select '2' ,'1'
union all
select '3' ,'2';

insert overwrite table tmp.ccc
select '1' ,'1'
union all
select '2' ,'0'
union all
select '3' ,'2';

答案:union all 会产生重复的行

-----Credit to Xunbi

 

 

ref:

https://www.cnblogs.com/sandbank/p/6408762.html

https://blog.csdn.net/qq_26442553/article/details/80866723

https://www.cnblogs.com/smartloli/p/4356660.html

https://www.jianshu.com/p/6a9a52550f3e

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值