hive join 数据倾斜 真实案例

2017/03/21 10:13
阅读数 295

Hive或者MR处理数据,不怕数据量大,就怕倾斜。Hive里大表join的时候,数据倾斜就是个很头疼的问题。本博主就遇到了一个真实案例,特意记录下来,有需要的同学可以参考

1.查了5个小时还没结束的sql语句

set mapred.reduce.tasks = 30;
insert overwrite directory 'xxx'
select
cus.idA,cus.name,addr.bb from tableA as cus
join tableB as addr
on cus.idA = addr.idB
  •  

很简单的一个hql语句,优化的空间也不是很大(例子中的addr数据量比cus小,应该讲addr放在前面驱动join)。tableA的量级为亿级,tableB的量级为几百万级别。就这么一个简单的sql,尼玛从上午十点半开始跑,跑到下午三点半还没有跑完。实在受不了了,kill掉了。

2.初步分析

首先上个查询过程中的图 
这里写图片描述

看到这种情况,稍微有点经验的同学第一反应肯定就是:卧槽,这尼玛肯定是数据倾斜了。没错,map早就完工了,reduce阶段一直卡在99%,而且cumulative cpu的时间还一直在增长,说明整个job还在后台跑着。这种情况下,99%的可能性就是数据发生了倾斜,整个查询任务都在等某个节点完成。。。

3.分析那部分数据产生了倾斜

问题既然已经定位了,那接下来就是需要解决问题了。正好不巧的是,集群这几天还出了一些状况。so,首先为了确认到底是集群本身的问题,还是代码的问题,先找了另外两个表,都是亿级数据。这两个表不存在数据倾斜的情况,join一把试了试,两分钟之内结果就出来了。万幸,说明这会集群已经没有问题了,还是查查数据跟代码吧。

代码本身很简单,那就沿着数据倾斜的方向查查吧。因为上面的两个表是根据id关联的,那如果倾斜的话,肯定就是id倾斜了哇。

set mapred.reduce.tasks = 5;
select idA,count(*) as num
from tableA
group by idA
distribute by idA
sort by num desc limit 10
  •  

结果为:

192928  5828529
2000000000496592833 2406289
18000   1706031
4000288 1386324
2000000003624295444 1201178
2000000001720892923 1029475
2000000002292880478 991299
2000000000736661289 881954
2000000000740899183 873487
2000000000575115116 803250
  •  

对于有上亿数据的一个表来说,这数据也算不上倾斜多厉害嘛。最多的一个key也就五百多万不到六百万。好吧,先不管了,再查一把另外一个表

set mapred.reduce.tasks = 5;
select idB,count(*) as num
from tableB
group by idB
distribute by idB
sort by num desc limit 10
  •  

结果也很快出来

192928  383412
18000   60318
617279581   23028
51010262    4643
4000286 3528
2000000000575115116 3218
1366173280  3012
4212339 2972
2000000002025620390 2704
2000000001312577574 2622
  •  

这数据倾斜,也不是特别严重嘛。

不过再把这两个结果一对比,尼玛恍然大悟。两个表里最多的一个key都是192928,一个出现了将近600万次,一个出现了将近40万次。这两个表再一join,尼玛这一个key就是600万*40万的计算量。最要命的是,这计算量都分配给了一个节点。我数学不太好,600万*40万是多少,跪求数学好的同学帮忙计算一下。不过根据经验来看的话,别说5个小时,再添个0也未必能算得完。。。

4.如何解决

既然找到了数据倾斜的位置,那解决起来也就好办了。因为本博主的真正需求并不是真正要算两个表的笛卡尔积(估计实际中也极少有真正的需求算600万*40万数据的笛卡尔积。如果有,那画面太美我不敢看),所以最easy的解决方案,就是将这些key给过滤掉完事:

set mapred.reduce.tasks = 30;
insert overwrite directory 'xxx'
select
cus.idA,cus.name,addr.bb from tableA as cus
join tableB as addr
on cus.idA = addr.idB
where cus.idA not in (192928,2000000000496592833,18000,4000288,2000000003624295444,2000000001720892923,2000000002292880478,2000000000736661289,2000000000740899183,2000000000575115116,617279581,51010262,4000286,1366173280,2000000002025620390,2000000001312577574)
  •  

将此代码重新提交,5min时间,job跑完收工!

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部