
长尾问题是分布式计算里最常见的问题之一。主要原因是因为数据分布不均,导致各个节点的工作量不同,整个任务就需要等最慢的节点完成才能完成。
Map长尾主要原因是某些Map Instance读取的数据量相对于其他的Instance多很多。
优化方法:
主要原因是分发键分布不均匀,存在热点数据(比如按照城市汇总用户量时,某一个城市的用户量占比超过60%,就可能出现Reduce长尾)。
优化方法:
比如:
SELECT CITY
,COUNT(DISTINCT USER_ID) AS USER_COUNT
FROM TABLE
GROUP BY CITY
;
如果长尾发生,可以考虑减少聚合时的数据量,比如可以先对CITY和USER_ID进行group by作为一个子查询,再在外层对CITY进行分组计算,具体优化语句为:
SELECt CITY
,COUNT(USER_ID) AS USER_COUNT
FROM (
SELECt CITY
,USER_ID
FROM TABLE
GROUP BY CITY
,USER_ID
)
GROUP BY CITY
;
Join长尾
JOIN阶段会将JOIN Key相同的数据分发到同一个Join Instance上执行处理。如果某个Key上的数据量比较多,会导致该Instance执行时间比其他Instance执行时间长。该JOIN Task的大部分Instance已执行完成,但少数几个Instance一直处于执行中,这种现象称之为Join长尾。
主要原因是表关联的键(key)分布不均匀,存在热点(本质就是关联的时候key值出现1对多的情况,这个多的部分就是热点)。
优化方法:
a、大小表关联,使用Mapjoin,将小表加载到内存,直接分发到Map Instance所在机器上,在读取的阶段就做hash-join。值得注意的是Mapjoin的小表大小有限制,而且小表必须要是从表(即不能是left join的左表,也不能是right join的右表)。
比如:假设表a左关联表b,b是小表:
SELECt a.id
,b.name
FROM a
LEFT OUTER JOIN b
ON a.id = b.id
;
转化为:
SELECt
a.id
,b.name
FROM a
LEFT OUTER JOIN b
ON a.id = b.id
;
如果大小表关联,而小表是主表,此时无法使用mapjoin,参考如下处理方式:
先将小表和大表利用MAP JOIN进行inner join,得到小表和大表的交集中间表,且这个中间表一定是不大于大表的(key倾斜程度与表的膨胀大小成正比)。然后小表再和这个中间表进行LEFT JOIN,这样操作的效果等于小表LEFT JOIN大表。
b、关联的两个表都比较大。
首先考虑去重。其次考虑将热点数据分开处理:先将主表热点Key取出,再用热点Key切分成热点数据和非热点数据两部分分别处理,最后合并。
假设a表的id存在很多空值,那么我们在关联的时候可以将关联条件转化。见下:
on a.id = b.id --转化为 on coalesce(a.id,rand()*9999) = b.id