Hive 数据倾斜 常用解决办法

1.数据倾斜的原因

数据分布不均匀,造成数据热点问题

2.数据倾斜的现象

  Hive任务进度长时间维持在99%或者100%的附近,进度好久没变化。通过查看任务监控页面Web,发现只有一个或者少数的reduce任务未完成,因为其处理的数据量和其他的reduce差异过大。单一reduce处理的记录数和平均记录数相差过多,导致最长时间远大于任务的平均时长。

3.数据倾斜情况

  • group by 不和聚集函数(count,sum等)配合使用时时候,维度过小,某值的数量过多
  • count(distinct)和distinct,特殊值过多,导致单个reduce慢
  • 小表关联大表join
  • 大表关联大表,空值过多

4.产生数据倾斜的原因

  • key分布不均匀(底层还是mr):因为分区是根据key值取哈希值再根据reduce取模
  • 业务数据本身的特性
  • 建表考虑不周全
  • 某些HQL语句本身就存在数据倾斜:count(distinct)

5.实际场景

(1)空值产生的数据倾斜
场景说明:
在日志中,常会有信息丢失的问题,比如日志中的 user_id,如果取其中的 user_id 和用户表中的 user_id 相关联,就会碰到数据倾斜的问题。
解决方案
  • user_id 为空的不参与关联
select * from log a join user b on a.user_id is not null and a.user_id = b.user_id 
union all 
select * from log c where c.user_id is null;
  • 赋予空值新的 key 值
select * from log a left outer join user b on 
case when a.user_id is null then concat('hive',rand()) 
else a.user_id end = b.user_id;
总结:
方法 2 比方法 1 效率更好,不但 IO 少了,而且作业数也少了,方案 1 中,log 表 读了两次,jobs 肯定是 2,而方案 2 是 1。这个优化适合无效 id(比如-99,’’,null)产 生的数据倾斜,把空值的 key 变
成一个字符串加上一个随机数,就能把造成数据倾斜的 数据分到不同的 reduce 上解决数据倾斜的问题。
改变之处: 使本身为 null 的所有记录不会拥挤在同一个 reduceTask 了,会由于有替代的 随机字符串值,而分散到了多个 reduceTask 中了,由于 null 值关联不上,处理后并不影响最终结果。
(2)大小表关联查询产生数据倾斜
注意:使用map join解决小表关联大表造成的数据倾斜问题。
map join 概念: 将其中做连接的小表(全量数据)分发到所有 MapTask 端进行 Join,从而避免了 reduceTask,前提要求是内存足以装下该全量数据
以大表 a 和小表 b 为例,所有的 maptask 节点都装载小表 b 的所有数据,然后大表 a 的 一个数据块数据比如说是 a1 去跟 b 全量数据做链接,就省去了 reduce 做汇总的过程。 所以相对来说,在内存允许的条件下使用 map join 比直接使用 MapReduce 效率还高些, 当然这只限于做 join 查询的时候。
在 hive 中,直接提供了能够在 HQL 语句指定该次查询使用 map join,map join 的用法是 在查询/子查询的SELECT关键字后面添加/*+ MAPJOIN(tablelist) */提示优化器转化为map join(早期的 Hive 版本的优化器是不能自动优化 map join 的)。其中 tablelist 可以是一个 表,或以逗号连接的表的列表。tablelist 中的表将会读入内存
map join 具体用法:
select /* +mapjoin(b) */ a.id aid, name, age from a 
join b 
on a.id = b.id;
select /* +mapjoin(ratings) */ a.title, b.rating from movies a 
join ratings b 
on a.movieid = b.movieid;
map join常用参数
-- 是否自动转为map join,默认为true
set hive.auto.convert.join = true;

-- 小表的最大文件大小默认为25000000,即25MB,当内存足够时,可以增大该数值,例如250MB
set hive.mapjoin.smalltable.filesize = 25000000;

-- 是否将多个mapjoin合并为一个,默认为true
set hive.auto.convert.join.noconditionaltask = true;

-- 多个mapjoin转换为1个时,所有小表的文件大小总和的最大值。当内存足够时,可以增大该数值。
set hive.auto.convert.join.noconditionaltask.size = 10000000;
(3)解决group by
set hive.groupby.skewindata=true;

进行负载均衡,当为true时,生成的查询计划会有两个MRjob

第一个job: 
map 的输出结果集合会随机分布到 reduce 中, 每个reduce 做部分聚合操作,并输出结果。这样处理的结果是,相同的 Group By Key 有可能分发到不同的reduce中,从而达到负载均衡的目的
第二个job:
MRJob再根据预处理的数据结果按照GroupBy Key分布到Reduce中(这个过程可以保证相同的GroupBy Key被分布到同一个Reduce中),最后完成最终的聚合操作。
第一个Job来解决数据倾斜的问题
(4)从key上解决
在 map 阶段将存在数据倾斜的key 预先分成多组(类似于HBase里面的预分区,也就是加盐),例如 aaa这个 key,map 时随机在 aaa后面加上 1,2,3,4 这四个数字之一,把 key 先分成四组,先进行一次运算,之后再恢复 key 进行最终运算。

6.MapReduce处理数据倾斜

  MapReduce提供Partitioner接口,它的作用就是根据key取hash值对reduce的数量取模,来决定当前的这对输出数据最终应该交由哪个reduce task处理。默认对key hash后再以reduce task数量取模。默认的取模方式只是为了平均reduce的处理能力,如果用户自己对Partitioner有需求,可以订制并设置到job上。
用hadoop程序进行数据关联时,常碰到数据倾斜的情况,这里提供一种解决方法。
源代码:
public int getPartition(K key, V value,
                          int numReduceTasks) {
    return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
  }
修改后:
public int getPartition(K key, V value,
                          int numReduceTasks) {
    return (((key).hashCode()+value.hashCode()) & Integer.MAX_VALUE) % numReduceTasks;
  }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值