HiveSql调优经验

背景

在刚使用hive的过程中,碰到过很多问题,任务经常需要运行7,8个小时甚至更久,在此记录一下这个过程中,我的一些收获

join长尾

背景

SQL在Join执行阶段会将Join Key相同的数据分发到同一个执行Instance上处理。如果某个Key上的数据量比较多,会导致该Instance执行时间比其他Instance执行时间长。其表现为:执行日志中该Join Task的大部分Instance都已执行完成,但少数几个Instance一直处于执行中,这种现象称之为长尾

长尾类别&优化方法

小表长尾

Join倾斜时,如果某路输入比较小,可以采用Mapjoin避免倾斜。Mapjoin的原理是将Join操作提前到Map端执行,这样可以避免因为分发Key不均匀导致数据倾斜。但是Mapjoin的使用有限制,必须是Join中的从表比较小才可用。所谓从表,即Left Outer Join中的右表,或者Right Outer Join中的左表。

热点值长尾

如果是因为热点值导致长尾,并且Join的输入比较大无法用Mapjoin,可以先将热点Key取出,对于主表数据用热点Key切分成热点数据和非热点数据两部分分别处理,最后合并。我们举一个电商的例子,假设我们需要计算所有商品的pv。我们有如下两张表

日志表 log 用户点击的日志, 包含商品的id p_id
商品表 product 包含商品名称 p_name, 商品id p_id

  • 取热点值, 取商品pv大于10000的商品到临时表
INSERT TABLE topk_product 
SELECT
	  distinct p_id
FROM
	(
		SELECT
			p_id,
			count(1) AS pv
		FROM log
		GROUP BY p_id
	) a
WHERE pv >= 10000
  • 取出非热点值和商品(product) join 得到非热点商品的pv
SELECT p.p_id
	, p.p_name
	, l.pv
FROM (
	SELECT p_id
		, p_name
	FROM product
) p
JOIN (
	SELECT /*+mapjoin(b)*/ a.*
	FROM (
		SELECT p_id
			, COUNT(1) AS pv
		FROM log
	) a
	LEFT OUTER JOIN (
		SELECT p_id
		FROM topk_product
	) b
	ON a.p_id = b.p_id
		AND b.p_id IS NULL
) l
ON p.p_id = l.p_id
  • 取出热点值和商品(product) join 得到热点商品的pv
SELECT p.p_id
	, p.p_name
	, l.pv
FROM (
	SELECT /*+mapjoin(b)*/ a.*
	FROM (
		SELECT p_id
			, p_name
		FROM product
	) a
	JOIN (
		SELECT p_id
		FROM topk_product
	) b
	ON a.p_id = b.p_id
) p
JOIN (
	SELECT /*+mapjoin(d)*/ c.*
	FROM (
		SELECT p_id
			, COUNT(1) AS pv
		FROM log
	) c
	JOIN (
		SELECT p_id
		FROM topk_product
	) d
	ON c.p_id = d.p_id
) l
ON p.p_id = l.p_id
  • union all 热点和非热点的数据

空值长尾

join时,假设左表(left_table)存在大量的空值,空值聚集到一个reduce上。由于left_table 存在大量的记录,无法使用mapjoin 。此时可以使用 coalesce(left_table.key, rand()*9999)将key为空的情况下赋予随机值,来避免空值集中造成长尾。

map长尾

Map端读取数据时,由于文件大小分布不均匀,一些map任务读取并处理的数据特别多,一些map任务处理的数据特别少,造成map端长尾。这种情形没有特别好的方法,只能调节splitsize来增加mapper数量,让数据分片更小,以期望获得更均匀的分配。

reduce长尾

由于Distinct操作的存在,数据无法在Map端的Shuffle阶段根据Group By先做一次聚合操作,减少传输的数据量,而是将所有的数据都传输到Reduce端,当Key的数据分发不均匀时,就会导致Reduce端长尾,特别当多个Distinct同时出现在一段SQL代码中时,数据会被分发多次,不仅会造成数据膨胀N倍,也会把长尾现象放大N倍。

我们用代码举个例子:

只有一个distinct 的情况

  • 原sql
SELECT D1
	, D2
	, COUNT(DISTINCT CASE 
		WHEN A IS NOT NULL THEN B
	END) AS B_distinct_cnt
FROM xxx
GROUP BY D1, 
	D2
  • 改后的sql
create table tmp1
as
select D1,D2,B,
count( case when A is not null then B end ) as B_cnt
from xxx
group by D1, D1, B


select D1,D2,
sum(case when B_cnt > 0 then 1 else 0 end) as B_distinct_cnt
from tmp1
group by D1,D2

多个distinct的情况

  • 原始sql
select D1,D2,
count(distinct case when A is not null then B end) as B_distinct_cnt ,
count(distinct case when E is not null then C end) as C_distinct_cnt 
from xxx group by D1,D2
  • 修改后的sql
create table tmp1
as
select D1,D2,B,
count( case when A is not null then B end ) as B_cnt
from xxx
group by D1, D1, B

create table tmp1_1
as
select D1,D2,
sum(case when B_cnt > 0 then 1 else 0 end) as B_distinct_cnt
from tmp1
group by D1,D2

create table tmp2
as
select D1,D2,C,
count( case when E is not null then C end ) as C_cnt
from xxx
group by D1, D1, C

create table tmp2_1
as
select D1,D2,
sum(case when C_cnt > 0 then 1 else 0 end) as C_distinct_cnt
from tmp1
group by D1,D2

select 
t1.D1,t1.D2,
t1.B_distinct_cnt,
t2.C_distinct_cnt
from tmp1_1 t1
left outer join tmp2_1 t2
on t1.D1=t2.D1 and t1.D2=t2.D2
posted @ 2017-05-18 17:02  段星星  阅读(15325)  评论(0编辑  收藏  举报