Hive数据倾斜解决方案

1、什么是数据倾斜?

由于数据分布不均匀,造成数据大量的集中到一点,造成数据热点。
任务进度长时间维持在 99%或者 100%的附近,查看任务监控页面,发现只有少量 reduce 子任务未完成,因为其处理的数据量和其他的 reduce 差异过大。单一 reduce 处理的记录数和平均记录数相差太大,通常达到好几倍之多,最长时间远大于平均时长。使得整体的任务进度时间由耗时最长的子任务决定。

2、产生数据倾斜的原因

A:key 分布不均匀

B:业务数据本身的特性

C:建表考虑不周全

D:某些 HQL 语句本身就存在数据倾斜

3、产生数据倾斜的业务场景及解决方法

(1)group by 产生数据倾斜
①根据业务合理调整分组维度
②加盐
③开启Map端聚合参数设置
group by时某一个key的数量过多导致对应的reducer负载过大时,采取map端部分聚合的策略。使用Map Combine进行map预聚合,可以减少磁盘IO传输

//是否在 Map 端进行聚合,默认为 
True set hive.map.aggr = true; 
//在 Map 端进行聚合操作的条目数目 
set hive.groupby.mapaggr.checkinterval = 100000; 
//有数据倾斜的时候进行负载均衡(默认是 false) 
set hive.groupby.skewindata = true;

(2) select语句中包含 count(distinct)时
①使用sum(1)… group by替代
②把倾斜的数据单独拿出来处理,最后union回去

(3)空值产生的数据倾斜
场景说明
在日志中,常会有信息丢失的问题,比如日志中的 user_id,如果取其中的 user_id 和用户表中的 user_id 相关联,就会碰到数据倾斜的问题。

解决方案
解决方案 1: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;

解决方案 2:赋予空值新的 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只读取了一次。
②这个优化适合把空值的 key 变成一个字符串加上一个随机数,就能把造成数据倾斜的 数据分到不同的 reduce 上解决数据倾斜的问题。

改变之处:使本身为 null 的所有记录不会拥挤在同一个 reduceTask 了,会由于有替代的新设的随机字符串值,而分散到了多个 reduceTask 中了,由于 null 值关联不上,处理后并不影响最终结果。

(2)不同数据类型关联产生数据倾斜
场景说明
用户表中 user_id 字段为 int,log 表中 user_id 为既有 string 也有 int 的类型, 当按照两个表的 user_id 进行 join 操作的时候,默认的 hash 操作会按照 int 类型的 id 进 行分配,这样就会导致所有的 string 类型的 id 就被分到同一个 reducer 当中

第一种:(hive0.11之前使用)将小表(或者是子查询)写在join左边

select field from smallTable inner join bigTable on ...

原因:位于Join操作符左边的表的内容会被加载进内存,在map端完成reduce。

第二种:(hive0.11之后会自动开启mapjoin)打开mapjoin功能(默认是打开的,需要检查):

       set hive.auto.convert.join=true; 
       // 设置mapjoin优化自动开启
       set hive.mapjoin.smalltable.filsize=25000000 
       // 设置小表不超过多大时开启mapjoin优化

★原因:将其中的小表分发到所有的MapTask端进行join,
避免在map端进行聚合,减少shuffle阶段的IO传输,提高效率

第三种:那么如果小表不大不小,那该如何处理呢???

select /*+mapjoin(x)*/* from log a
left outer join (
 select /*+mapjoin(c)*/ d.*
 from ( select distinct user_id from log ) c join users d on c.user_id = d.user_id
) x
on a.user_id = x.user_id;

将log的user_id这一列单独提取出来,然后再与users进行join,这样就可以把null值进行过滤筛选,起到把表缩小的作用,然后再把log表与刚刚过滤出来的表进行left outer join ,这样不用MapReduce,就能提取到自己想要的数据。

分区表:
创建一个分区,把1张或多张表放入到这个分区中,这样可以在查询时避免进行全表查询,从而提高查询效率,分区表在HDFS上的表现形式是目录。
分桶表:
分桶表是一种更细粒度的数据分配方式,可以对一张表的某一列进行分桶,让该列数据按照哈希取模的方式随机、均匀地分发到各个桶文件中。这样一方面可以提高查询效率,另一方面用于数据的抽样,方便进行数据测试。在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便。分桶表在HDFS上的表现形式是文件.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值