1、背景
有上游任务存了这样两列数据 uid和expid_list,
uid1, expid1;expid2
uid2, expid1;expid3;expid4
我们可以使用lateral view + explode把数据处理成这样的形式
uid1, expid1
uid1, expid2
uid2, expid1
uid2, expid3
uid2, expid4
2、排查过程
要优化的代码
select
uid,
exp_id
from
log.exp_xxx lateral view explode(split(expid_list, '\073')) mycol as exp_id
要打散的字段长度不一
要优化的地方就是把数据给打散,但是发现直接就倾斜了,因为expid_list的长度有长有短,有的task处理的expid_list很长,但有的有很短,于是有的task执行时间很长,有的很短。
这是generate的stage。可以看到有的task read write很大,但有的很小,其实就是expid_list长短不一造成的。
倾斜的stage 就是generate的过程
一开始优化是将原始数据按expid_list的长度做了区别处理
select
uid,
exp_id
from
log.exp_xxx lateral view explode(split(expid_list, '\073')) mycol as exp_id
改成了下面这样,这样做generate的stage会从1个变成2个,而且长的单独做长的,短的做短的,单一stage内的倾斜就解决了。
select
uid,
exp_id
from
log.exp_xxx lateral view explode(split(expid_list, '\073')) mycol as exp_id
where
length(expid_list) >= xxx
union all
select
uid,
exp_id
from
log.exp_xxx lateral view explode(split(expid_list, '\073')) mycol as exp_id
where
length(expid_list) < xxx
上游文件数太少
但是发现任务还是不怎么快,又看了下UI里的DEBUGE,发现这个任务明明配了minExecurtor参数几百,但是在做lateral view的时候只分了几十个,再看了下,其实是上游就只有几十个文件。
这个操作发生在第一步,是个map,executor分配的数量就和上游文件数一样。所以executor个数太少了,我们把上游文件重分布一下。就能充分利用起多executor执行的优势了。
select
uid,
exp_id
from
(select
uid,
expid_list
from
log.exp_xxx
distribute by
uid) a lateral view explode(split(expid_list, '\073')) mycol as exp_id
where
length(expid_list) < xxx
结果
优化了这两点之后,任务从需要耗时7000s变成了1000s
3、问题抽象
- 上游文件个数太少,第一步又是map操作,那spark默认应该是单个excutor对单个文件,这样就可能出现效率问题。
- lateral view + explode,需要管制打散字段的分布情况。
网友评论