Hive在分布式运行的时候最害怕的是数据倾斜,这是由于分布式系统的特性决定的,因为分布式系统之所以很快是由于作业平均分配给了不同的节点,不同节点同心协力,从而达到更快处理完作业的目的。
Hive中数据倾斜的原因:
- 数据在分布式节点上分部不均衡
- join时某些key可能特别大(常见null值)
- group by 时某个值可能特别多
- count(distinct key...)时有可能会出现数据倾斜,因为其内部处理会进行group by 操作
- join
join时key最好是分散的,如果一个key的数据量特别大,有可能会出现数据倾斜和OOM。一个核心就是小表join大表,可以在reduce阶段,左侧的小表全部加载到内存,降低OOM的风险
- 大表join大表
数据倾斜,例如null值。解决办法一般是打散null值,例如使用随机数等。
- mapjoin
小表join(超)大表的时候,可以采用mapjoin 的方式把小表全部加载到mapper端的内存中。不会自动进行mapjoin,需要设置:
set hive.auto.convert.join=true; //hive在进行join的时候会判断左表的大小来决定是否进行mapJoin set hive.mapjoin.smalltable.filesize=128000000 //hive在进行join的时候会判断左表的大小来决定是否进行mapJoin的大小阈值 字节数 set hive.mapjoin.cache.numrows=1000000 //hive在进行join的时候会判断左表的大小来决定是否进行mapJoin的大小阈值--数据行数
上述参数可以根据实际的硬件机器的内存进行调整,对性能有至关重要的影响,因为没有了shuffle,对于mapjoin我们能够使用mapper端JVM中多大的内存?
set hive.mapjoin.followby.gby.localtask.max.memory.usage=0.55 //百分比 set hive.mapjoin.localtask.max.memory.usage=0.9 //百分比
- group by
可以设置在Mapper端进行部门聚合,最后在reduce端进行全局聚合set hive.map.aggr=true; //默认开启, set hive.groupby.mapaggr.checkinterval=1000000; //在Map端进行聚合操作的条目数 //防止数据倾斜 set hive.groupby.skewindata=true; //会产生Mapper-Reducer-Reducer的结构
生成查询计划时,实际上会生成两个job,第一个job会通过自己的算法打散倾斜的key并进行聚合操作并保留结果,第二个job会完成全部的Group by 操作,相当于Mapper-Reduce-Reduce的结构。(第一个会把Mapper的输出随记分布到Reduce中,每个Reduce做部分聚合并且保存结果,这样导致相同的groupby key分配到不同的Reduce上,一定程度上避免数据倾斜,接下来另外一个Job根据前一个Job预处理数据的结果再进行Group By到Reduce中)
- count(distinct ) 如果某个值特别多,容易产生数据倾斜。
解决思路:
在查询语句中,例如对null值进行过滤,在结果上加1。 count(uid) (uid中去掉了值为null)的记录,所以在最后的结果 cnt 的基础上加1, 即cnt+1