Hive常见优化

一、数据倾斜

1、什么是数据倾斜?Hadoop 框架的特性决定最怕数据倾斜

由于数据分布不均匀,造成数据大量的集中到一点,造成数据热点。

节点间数据分布不均衡,会造成 map 端每个 map 任务的工作量不同,即 map 端数据倾斜。

Map-reduce,把相同 key 提交给同一个 reduce,如果 key 不均衡就会造成不同的 reduce 的

工作量不同。 以京东首页活动为例,曝光率大的是大活动,曝光率小的是小活动:

假如 reduce1 处理的是小活动,reduce2 处理大活动,reduce2 干的活比其他 reduce 多很多, 会出现其他 reduce 执行完毕了,reduce2 还在缓慢执行。

症状:map 阶段快,reduce 阶段非常慢; 某些 map 很快,某些 map 很慢; 某些 reduce 很快,某些 reduce 奇慢。

如下情况: A、数据在节点上分布不均匀

B、join 时 on 关键词中个别值量很大(如 null 值)

C、count(distinct ),在数据量大的情况下,容易数据倾斜,因为 count(distinct)是按 group by 字

段分组,按 distinct 字段排序。

其中 A 无法避免。B 见后边的 Join 章节。C 语法上有时无法避免

如何解决数据倾斜?实际上是没办法避免的,这里的解决只是个别情况起效: 有数据倾斜的时候进行负载均衡
set hive.groupby.skewindata = false;

当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处 理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的 目的;第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这 个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操 作。

二、 Join 、MapJoin、Group by

Join

按照 join 的 key 进行分发,而在 join 左边的表的部分数据会首先读入内存,如果左边表 的 key 相对分散(或少,分散的意思是相同 key 的数据量小),join 任务执行会比较快;而如 果左边的表 key 比较集中(key 的大小量级分化),而这张表的数据量很大,那么数据倾斜就 会比较严重。

Map 阶段同一 Key 数据分发给同一个 reduce。

Join 原则:

小表 Join 大表,原因是在 Join 操作的 Reduce 阶段(不是 Map 阶段),位于 Join 左

边的表的内容会被加载进内存,将条目少的表放在左边,可以有效减少发生内存溢出的几率。 多个表关联时,最好分拆成小段,避免大 sql(无法控制中间 Job)。

多表 Join on 条件相同时合并为一个 Map-Reduce,做 OUTER JOIN 的时候也是一样,查看 执行计划 explain
比如查询,3 个表关联:

select pt.page_id,count(t.url) PV from rpt_page_type pt
join

(select url_page_id,url from trackinfo where ds='2013-10-11') t on pt.page_id=t.url_page_id
join

(select page_id from rpt_page_kpi_new where ds='2013-10-11') r on t.url_page_id=r.page_id
group by pt.page_id;

比较 2 个表关联:

select pt.page_id,count(t.url) PV from rpt_page_type pt
join

(select url_page_id,url from trackinfo where ds='2013-10-11') t on pt.page_id=t.url_page_id
group by pt.page_id;

利用这个特性,可以把相同 join on 条件的放在一个 job 处理。

如果 Join 的条件不相同,如:

5000 万,纬度表 每日 2 亿,按日增量表

INSERT OVERWRITE TABLE page_pv select pt.page_id,count(t.url) PV from rpt_page_type pt

join

(select url_page_id,url,province_id from trackinfo where ds='2013-10-11') t on pt.page_id=t.url_page_id
join

(select page_id,province_id from rpt_page_kpi_new where ds='2013-10-11') r on t.province_id=r.province_id
group by pt.page_id;

大表 Join 大表

访户未登录时,日志中 userid 是空,在用 user_id 进行 hash 分桶的时候,会将日志中 userid 为空的数据分到一起,导致了过大空 key 造成倾斜。

解决办法:

把空值的 key 变成一个字符串加上随机数,把倾斜的数据分到不同的 reduce 上, 由于 null 值关联不上,处理后并不影响最终结果

案例:

End_user

Trackinfo

原写法:

select u.id,t.url,t.track_time from end_user u

join

(select end_user_id,url,track_time from trackinfo where ds='2013-12-01') t on u.id=t.end_user_id limit 2;

调整为:

select u.id,t.url,t.track_time from end_user u

join

(select case when end_user_id='null' or end_user_id is null

then cast (concat('00000000_',floor(rand()*1000000)) as bigint) else end_user_id end end_user_id ,
url,track_time


from trackinfo where ds='2013-12-01') t on u.id=t.end_user_id limit 2;
此例子只是为了说明原理。

当前这个场景不需要这么麻烦,如下即可: select u.id,t.url,t.track_time

from end_user u

在这里插入图片描述

select   ...
from
        (select   *
         from     tbcdm.dim_tb_itm
         where    ds='${bizdate}'
         )son1
left outer join
        (select  *
         from    tbods.s_standard_brand
         where   ds='${bizdate}'
         and     status=3
         )son2
on       coalesce(son1.org_brand_id,rand()*9999)=son2.value_id

count(distinct)的优化策略

--  第三层SELECT
SELECT
  SUM(s.mau_part) mau
FROM
(
  -- 第二层SELECT
  SELECT
    tag,
    COUNT(*) mau_part
  FROM
  (
      -- 第一层SELECT
    SELECT
      uuid, 
      CAST(RAND() * 100 AS BIGINT) tag  -- 为去重后的uuid打上标记,标记为:0-100之间的整数。
    FROM detail_sdk_session
    WHERE partition_date >= '2016-01-01' AND partition_date <= now
    GROUP BY uuid   -- 通过GROUP BY,保证去重
   ) t
  GROUP BY tag
) s
;

第一层SELECT:对uuid进行去重,并为去重后的uuid打上整数标记
第二层SELECT:按照标记进行分组,统计每个分组下uuid的个数
第三层SELECT:对所有分组进行求和
上面这个方法最关键的是为每个uuid进行标记,这样就可以对其进行分组,分别计数,最后去和。如果数据量确实很大,也可以增加分组的个数。例如:CAST(RAND() * 1000 AS BIGINT) tag

这个可以很好的利用分布式的原理进行统计

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值