全站按月流量数据倾斜处理过程
全站按月流量需求,表adm_s14_ol_site_m,因为数据量太大(临时表1整月的数据条数达到48亿多),导致脚本开发完成后数据倾斜,
程序执行时reduce到96%后进度很慢,到99%就一直不能执行完毕,最长一次尝试执行时间达到14小时都未执行完;
下面描述该脚本数据倾斜的处理过程:
1. 该需求是按站点汇总的,统计站点代码有1—6六个,1为京东主页,且数据量教其余五个之和要大很多,所以脚本最初开发时就union all 分成了两部分:
select
substring('"""+last_month + """', 1, 7) as stat_month,
result.*
from
(
select * from awhere substr(web_site_id, 10, 1) <> '1' --
-- 不是京东主页的
union all
select * from bwhere substr(web_site_id, 10, 1) = '1' /*--
-- 是京东主页的*/
)
result
union all 两部分仍旧数据倾斜,之前周云峰开发的按日汇总的,是这么处理的,由于一天的数据量教一个月的数据量小很多,所以他那个脚本gdm_s14_ol_site_day_sum.py虽然存在数据倾斜,但是能执行出结果;
2. sql前面加set hive.groupby.skewindata=true;
有数据倾斜的时候进行负载均衡,当选项设定为 true,生成的查询计划会有两个 MR Job。
第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;
第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。该方法能不能处理这个脚本的数据倾斜问题。
3. 脚本中很多指标的计算都是count(distinct),当有大量相同特殊值时就容易出现数据倾斜,改写脚本,采用sum()group by的方式来替换count(distinct)完成计算,拆开后有效解决了数据倾斜问题,例如:
select
count(distinctsession_id) as visits,
count(distinct
case whenurl_second_cate_id = 35 or url_second_cate_id = 38 then
session_id end) asSucc_Create_Ord,
count(distinct
case whenurl_first_cate_id = 9 and item_id is not null then session_id
end) as Item_Visits,
count(distinct
case whenurl_second_cate_id = 53 then session_id end) as
Add_To_Cart_visits
from
gdm.gdm_online_log
where
dt >='"""+ last_month + """'
and dt <='"""+ current_day+ """';
改为:
select
stat_site_cd ,
sum(visits) asvisits ,
sum(Succ_Create_Ord)as Succ_Create_Ord,
sum(Item_Visits) as Item_Visits,
sum(Add_To_Cart_visits)as Add_To_Cart_visits
from
(
select
stat_site_cd ,
'1' as visits,
count(distinct
case whenurl_second_cate_id = 35 or url_second_cate_id
= 38 then session_idend)
as Succ_Create_Ord,
count(distinct
case when url_first_cate_id= 9 and item_id is not null
then session_id end)as Item_Visits,
count(distinct
case whenurl_second_cate_id = 53 then session_id end)
asAdd_To_Cart_visits
from
tmp.tmp_adm_s14_ol_site_m_01
where
dt ='"""+ ht.data_day_str + """'
group by
stat_site_cd,
session_id
)
tmp
group by
stat_site_cd;