hive解决数据倾斜问题_如何解决Hive中经常出现的数据倾斜问题

Hive 执行 MapReduce 过程中经常会出现数据倾斜问题,具体表现为:作业经常在 Reduce 过程完成 99% 的时候一直停留,最后 1% 一直保持很久才完成。这种情况是由于:在数据量很大的情况下,在 MapReduce 的 Shuffle 过程执行后,key值分布到 Reducer 节点不均匀;有的 key 少,哈希后被分在不同节点中没有问题,但是有的 key 特别的多,过于集中了,全被分配在一个 Reducer 节点,所以其他的 Reducer 都执行完了都在等这个量大的 key 值,这就导致了数据倾斜。通俗的话来讲就是,一堆干完活的人等那个干的最慢的人,不是因为那个人能力差,大家能力水平都相同,是他真的干不完……被分配太多了,别人做完了也没法帮忙。

所以这也违背了 MapReduce 方法论产生的核心思路,不怕活儿多,活多咱们可以多分配人手;最怕的就是活分配不均匀,有人干的多,有人干的少,出现时间上的浪费。这些工作在 MapReduce 过程中,往往都出现在 GROUP

BY 等分组,各种类型 JOIN 过程中。常见产生数据倾斜的原因大致有以下几种:大量空值

某个 key 值大量重复

不同数据类型关联

COUNT(DISTINCT)

接下来详细介绍各种出现以及如何避免这类问题的出现。

二、问题分类及解决思路

1.大量空值产生数据倾斜

解决思路:利用随机数将空值进行随机填充,注意:不要让随机数碰撞到其他值,提前要测试下是否有膨胀现象发生,之所以选择以下例子是因为流量表中往往存在无”主键”的情况,c端用户不登录,就不会在流量表记录用户唯一值。例如:

优化前:1select * from click_log a left join users b on a.user_id = b.user_id;

优化方法1. 让 user_id 为空的不参与关联,1

2

3select * from click_log a join users b on a.user_id is not null and a.user_id = b.user_id

union all

select * from click_log a where a.user_id is null;

优化方法2. 让随机数冲散堆积在一个人 Reduce 中的很多 null 值,1

2

3select * from click_log a left join users b

on case when a.user_id is null then concat('dp_hive',rand())

else a.user_id end = b.user_id;

解释:如果key均为空值,大量的key会分布在同一个Reduce节点上;在其他Reduce节点完成ReduceTask后,存在大量空值的Reduce还未完成Task,因此产生数据倾斜。concat('dp_hive',rand())是为了把空值变成一个字符串加上随机数的,把null值倾斜的数据分布在不同Reduce节点上,间接把倾斜的数据分布在不同Reduce上。

2.某 key 值大量重复产生数据倾斜

其实,当key值非空,但某个key出现大量重复的情况的解决方案和上述空值情况相同,均为引入随机数进行优化。

优化前:1

2select a.key as key, count(b.pv) as pv from test_table1 a inner join test_table2 b

on a.key = b.key group by 1;

优化后:1

2

3

4

5

6

7

8

9

10select a.key as key, b.pv as pv

from

(select key from test_table1) a

inner join

(select key, sum(pv) as pv

from

(select key, round(rand()*1000) as rnd, count(1) as pv from test_table2 group by 1,2) tmp

group by 1;

) b

on a.key = b.key;

解释: round(rand()*1000) as rnd –> sum(pv) 加入随机,将本来ReduceTask在一组的key,拆分成多组进行处理,增加并发度。

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

用户表user_id字段为 bigint,click_log 表中user_id字段既有string类型也有bigint类型。当按照user_id进行两个表的join操作时,默认的hash操作会按bigint型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reduce中,例如:

优化前:1

2select * from click_log a left join users b

on a.user_id = b.user_id;

优化后:1

2select * from users a left join click_log b

on a.user_id = cast(b.user_id as string)

4.COUNT(DISTINCT) 产生数据倾斜

首先,说说 group by 和 distinct 的区别。我在工作过程中实践过很多场景,发现在处理时间上并没有什么本质区别,都是在一个 job 中,出现一个 reduce 过程。那么为什么一定要要强调用 group by 代替 distinct 呢? 我在网上搜了很多资料,都没有说清楚这个问题的本质。接下来我来说说我的理解:

其实,大部分情况我们根本不用代替,因为 大部分的情况 数据是不倾斜的。

举个栗子,我们经常计算每个用户这一年变换手机号次数,Email次数等等,当然不会倾斜,因为谁没事儿老去更换手机号呢!

所以,数据不倾斜的时候基本上两个过程是等价的。但是,一旦出现数据倾斜,还是要代替一下,因为 group by 覆盖到了很多 hive 计算引擎参数设置,例如:set hive.groupby.mapaggr.checkinterval = 100000; – 这个是 grby 的键对应的记录条数超过这个值则会进行优化

set hive.groupby.skewindata = true; – 如果 grby 过程中出现倾斜,设置成 true,会自动将相同的reduceKey进行 Hashing

除此之外,join 也涉及到了一些,如下:set hive.skewjoin.key = 100000; – 这是 join 的键对应的记录条数超过这个值则会进行优化;

set hive.optimize.skewjoin = true; – 如果是 join 过程中出现数据倾斜,设置成 true

其次,count(distinct) 会压力都积压在一个 reduce 过程中,导致一个job处理太多数据,导致数据倾斜,改成 count(1) + group by 的处理方式,这样会将整个过程解耦,进而分解整体过程中的压力。

最后,有一点自己在写 Hive Sql 时候的体会,代码可读性和性能优化,一定都要考虑,过于复杂的代码逻辑一定要加注释,增强代码可读性,举个栗子:1select a, sum(b), count(distinct c), ... from T group by a;

优化后本应该是:1

2

3

4

5

6

7

8select a, sum(b) as b,count(c) as c, ... from

(

select a, b, null as c, ... from T group by a,b

union all

select a, 0 as b, c, ... from T group by a,c

union all

...

) tmp1 group by a;

其实,企业级的 hive 在处理 T+1 数据时,处理的速度并不会相差太远,即优化程度不高的情况下,建议保留业务逻辑。

你可能感兴趣的:(hive解决数据倾斜问题)