Hive数据倾斜总结

前言

        数据倾斜是大数据处理不可避免会遇到的问题,那么在Hive中数据倾斜又是如何导致的?通过本片本章,你可以清楚的认识为什么Hive中会发生数据倾斜;发生数据倾斜时我们又该用怎么的方案去解决不同的数据倾斜问题。


一、什么是Hive的数据倾斜

        数据倾斜顾名思义,因为数据分布不均匀产生的数据倾斜。在Hive中的数据倾斜,通常表现在MapReduce任务处理时,不同节点并行处理数据过程中,由于某一节点的数据远多于其他节点的数据,而导致此节点处理数据的时间远超其它节点甚至导致任务失败。


二、发生数据倾斜的原因

  • key分布不均
  • 建表考虑不周
  • 业务数据本身的特性
  • 某些HSQL语句本身存在数据倾斜

三、如何解决实际案例中的数据倾斜

1.空值过多

        在采集数据的过程中,数据往往有缺失的情况,而缺失字段又正好是我们关联表时所使用的关联字段,此时MapReduce过程中,如果空值过多,那么shuffle时会将空值数据放入同一个分区,那么对应的reduce节点所需要处理的数据远大于其他节点,此时就产生了严重的数据倾斜;

解决方法:

        a. 将空字段数据和非空字段数据分开检索,并用union all合并查询数据;

eg.

select * from order a 
inner join
	customer b
on
	a.user_id = b.user_id
where 
	a.user_id is not null

union all 

select * from order a where a.user_id is null

        b.使用外连接关联两表,关联时对关联字段为空的添加名称,使其不为空,从而避免非空字段过多处于同一reduce中的情况;

eg.

select * from order a 
left outer join
	customer b
on
	case when a.user_id is null then concat('order_id',rand()) else a.user_id end = b.user_id

TIPS:以上两种方式更推荐第二种方式;相较于第一种,第二种IO读写次数小于第一种,而且第二种的执行计划也比前一种更少(作业数更少),执行起来比前一种效率更高。

2.groupby维度过小

        比如某市统计本市居民人数和非本市人数时,按照居民信息的地级市来分组,假设当地人数是非本市人数的十倍,那么reduce本市的人数计算将远超于非本市人数计算的时间;

解决方法:

a.开启map端聚合&数据倾斜参数:

set hive.map.aggr=true
set hive.groupby.skewindata=true

b.加盐处理:将本市的地级市字段增加一个1~10的随机数(前面本市人数是非本市的十倍),之后分组聚合,最后将本市相同的聚合一次即可;

with info as
select 
	a.id,
	case when a.city=="nanjing" then concat(city,floor(1+rand()* 10)) else city end city
from
	resident_info a

select a.city,count(a.id) from info b group by a.city

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

#表现为在做表关联时,关联字段类型不一致,a表中的关联字段为int类型,而b表中的关联字段既有int类型又有string类型,分区时同为string类型的会被分至同一分区中。

解决方法: 将数据类型不一致的字段转换为string类型;

selct
	*
from
	customer  a
left join 
	order b
on
	b.user_id = cast(a.user_id as string)

4.小表join大表

        map端的join效率高,并行度高 ,不容易产生数据倾斜;map端的join擅长大小表关联。

        在 hive0.11 版本前需要手动指定查询走mapjoin,之后的版本在小表满足参数的情况下,自动map端join.

手动指定map端join的方式:

select /* +mapjoin(a) */ a.id aid, name, age from student a join score on a.id = b.id;

控制MapReduce大小表map端join的参数:

set hive.auto.convert.join=true;  //设置 MapJoin 优化自动开启
set hive.mapjoin.smalltable.filesize=25000000//设置小表不超过25M开启 mapjoin 优化

eg:(下面我们通过explain实例来详细展示HQL运行时底层如何实现map端join)

sql语句:

--  	查询每个年级,每个班,男生女生的人数
explain select
    t.grade_id,
    t.caption,
    t.gender,
    count(t.sid) count
from(
    select
        b.grade_id,
        b.caption,
        a.gender,
        a.sid
    from
        student a
    inner join
        class b
    on
        a.class_id = b.cid)t
group by
    t.grade_id,t.caption,t.gender

执行计划解析:


STAGE PLANS:
  Stage: Stage-5
    Map Reduce Local Work
      Alias -> Map Local Tables:
        t:b 
          Fetch Operator  //客户端获取数据
            limit: -1
      Alias -> Map Local Operator Tree:
        t:b 
          TableScan  //浏览table b:class
            alias: b
            Statistics: Num rows: 1 Data size: 207 Basic stats: COMPLETE Column stats: NONE
            Filter Operator //过滤操作符
              predicate: cid is not null (type: boolean)
              Statistics: Num rows: 1 Data size: 207 Basic stats: COMPLETE Column stats: NONE
              HashTable Sink Operator //因为小于25M,所以将小表存储到本地的hashTable中
                keys:
                  0 class_id (type: int)
                  1 cid (type: int)

  Stage: Stage-2
    Map Reduce
      Map Operator Tree:
          TableScan 	//浏览table a:student
            alias: a
            Statistics: Num rows: 2 Data size: 315 Basic stats: COMPLETE Column stats: NONE
            Filter Operator 	//过滤操作符
              predicate: class_id is not null (type: boolean) 	//class_id不为空
              Statistics: Num rows: 1 Data size: 157 Basic stats: COMPLETE Column stats: NONE
              Map Join Operator //map端join,并且为inner join
                condition map:
                     Inner Join 0 to 1  
                keys: 	//指定join时对应字段关联
                  0 class_id (type: int)
                  1 cid (type: int)
                outputColumnNames: _col0, _col2, _col8, _col9
                Statistics: Num rows: 1 Data size: 172 Basic stats: COMPLETE Column stats: NONE
                Select Operator 	//查询操作符
                  expressions: _col9 (type: int), _col8 (type: string), _col2 (type: string), _col0 (type: int)
                  outputColumnNames: _col0, _col1, _col2, _col3
                  Statistics: Num rows: 1 Data size: 172 Basic stats: COMPLETE Column stats: NONE
                  Group By Operator 	//分组操作符
                    aggregations: count(_col3) 	//使用聚合函数count,字段_co13,对应sql语句中的sid
                    keys: _col0 (type: int), _col1 (type: string), _col2 (type: string) 	//分组key字段
                    mode: hash 	//聚合方式:用字段哈希值进行分组聚合
                    outputColumnNames: _col0, _col1, _col2, _col3
                    Statistics: Num rows: 1 Data size: 172 Basic stats: COMPLETE Column stats: NONE
                    Reduce Output Operator //reduce输出操作符
                      key expressions: _col0 (type: int), _col1 (type: string), _col2 (type: string)
                      sort order: +++  //key字段对应排序方式
                      Map-reduce partition columns: _col0 (type: int), _col1 (type: string), _col2 (type: string) //Map-reduce任务分区字段
                      Statistics: Num rows: 1 Data size: 172 Basic stats: COMPLETE Column stats: NONE
                      value expressions: _col3 (type: bigint)
      Local Work:
        Map Reduce Local Work
      Reduce Operator Tree:
        Group By Operator
          aggregations: count(VALUE._col0)
          keys: KEY._col0 (type: int), KEY._col1 (type: string), KEY._col2 (type: string)
          mode: mergepartial
          outputColumnNames: _col0, _col1, _col2, _col3
          Statistics: Num rows: 1 Data size: 172 Basic stats: COMPLETE Column stats: NONE
          File Output Operator
            compressed: false
            Statistics: Num rows: 1 Data size: 172 Basic stats: COMPLETE Column stats: NONE
            table:
                input format: org.apache.hadoop.mapred.TextInputFormat
                output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
                serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe

  Stage: Stage-0
    Fetch Operator //客户端获取数据
      limit: -1	//设置抓取数据长度,-1表示全部抓取
      Processor Tree:
        ListSink

执行计划流程图:

在这里插入图片描述

总结:大小表关联时,如果满足参数条件,小表自动转为HashTable加入到缓存中,之后将数据发送到各个map端join大表


5.大表join大表

        可以参考小表join大表的方式,将其中的一个大表拆为多个小表,然后将每个小表分别mapjoin;但也存在大表数据过大无法拆分的情况,遇到这种情况,可以用下面的方法来处理;

        将有订单的用户信息提取出来之后进行和订单表的关联,这样就过滤到了一部分没有下单的顾客;从而减少了数据量;

select /*+mapjoin(b)*/* from order a
left outer join (
 select /*+mapjoin(c)*/ d.*
 from ( select distinct user_id from order) c join cusotmer d on c.user_id = d.user_id
) b
on a.user_id = b.user_id;

PS:如果有写错或者写的不好的地方,欢迎各位大佬在评论区留下宝贵的意见或者建议,敬上!如果这篇博客对您有帮助,希望您可以顺手帮我点个赞!不胜感谢!

原创作者:wsjslient

作者主页:https://blog.csdn.net/wsjslient


  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值