不管是实际工作还是面试中,数据倾斜的问题永远都是不可避免会遇到的。看了一些文章,发现有的给出的hive数据倾斜解决方法里有函数关键词错误的,下面对hive中的数据倾斜用HQL解决的例子做了总结:


例如:
日志-表A :user_id(用户id) event(行为)state(状态) time(时间戳)

用户-表B :user_id(用户id)sex(性别)


一、两个表相互关联,其中一个表批量出现NULL:
现在做  A left join B
(1)假如表A出现了大批量的user_id为NULL,会碰到数据倾斜的问题,而我们又要排除表A中为NULL的数据,那么可以:

select A1.* , B.*
 from
 (select * from A where user_id is not NULL ) A1
 left join B
 on A1.user_id = B.user_id

这里在left jorn 前就过滤点NULL

(2)假如表A出现了大批量的user_id为NULL,会碰到数据倾斜的问题,而我们又要查出A中为NULL的数据,那么可以:
   给为NULL的user_id替换一个随机数,考虑到user_id可能是数字,为了防止替换的随机数被匹配到,可以增加一个特殊字符'v_':

select A.* , B.*   
 from A 
 left join B   
 on (case when A.user_id is NULL then concat('v_',rand()) else A.user_id end) = B.user_id;

这里把user_id为NULL变成了随机数,就不存在NULL都在一起的情况了。也就解决了数据倾斜

二、两个表互相关联,其中一个是小表(一般小于二三十M),这种情况可以用 map jorn 解决数据倾斜问题,把小表放在内存中

(1)假如 B 表为小表

select /*+ MAPJOIN(B)*/  A.*,B.* 
 from  
 A  
 left join B
 on Auser_id =B.user_id




(2)假如B 表很大,不能放入内存。并且A表日志表也很大,但是A表里面全是作弊用户刷量,也就是说A表里面虽然有很多条记录,但是实际 都是几个或是几十个,几百个相同的user_id的记录。那么我们就可以创造出一个小表,然后运用 map jorn:

select /*+mapjoin(C)*/ A.*,C.*
 from A  
 left join 
   (
     select  /*+mapjoin(A1)*/ B.*
       from ( select distinct user_id from A ) A1
       join B 
       on A1.user_id = B.user_id
    ) C
   on A.user_id = C.user_id;




  **这里的C表是个小表--因为之前说了,A表里面都是重复user_id的记录,经过去重 也就几个或是几十个,几百个,关联B 表的信息后 生产C表。然后最后一层  把 C表当小表 运用map jorn


三、两张大表关联,key分布不均


  假如:日志A表中的user_id 有个别几个是 作弊刷量用户(这几个user_id的日志记录条数很多),其他的user_id 正常,这样和用户B表关联可能就会出现数据倾斜。解决方案:
  可以打散表A。

select


 COALESCE(D.user_id,E.user_id),
 COALESCE(D.event,E.event),
 COALESCE(D.state,E.state),
 COALESCE(D.time,E.time),
 D.sex,
 D.age


 from 
 (
 select B.user_id as B_user_id ,B.sex as sex ,B.age as age , A.user_id as user_id ,A.event as event , A.state as state ,A.time as time  from 
 B
 left join 
 (select * from A TABLESAMPLE(BUCKET 1 out of 2 on time) ) C
 on B.user_id = C.user_id 
 ) D


 left join 


 (select * from A TABLESAMPLE(BUCKET 2 out of 2 on time) ) E
 on D.B_user_id = E.user_id




这是以B表为主表。如果想要得到A表信息。最后可以在加where 条件。


**这里是把A表,按照time字段Hash取模,分成了2 个桶。目的是为了把那些相同的user_id 记录打散,分在两个表里面。(特殊情况下,不好根据字段打散时,也可以新加一列随机数字段,根据随机数打散)
经过两次left jorn 。 


COALESCE函数类似 数据库的NVL函数。
示例 
下面的语句返回值 258。 
SELECT COALESCE( NULL, 258, 1311, 0 ) 
备注 
如果所有参数均为 NULL,则 COALESCE 返回 NULL