“ 数仓面试中,关于HQL经常会被问到这几个问题:数据倾斜、HQL优化,及其常见的开窗函数(现场会让你写SQL)。这一节,我们来看下HQL数据倾斜这块的问题。”
01
—
hive 数据倾斜基本概念及其原理
什么是hive数据倾斜
由于数据分布不均匀,造成数据大量的集中到一点,造成数据热点
主要表现

任务进度长时间维持在 99%或者 100%的附近,查看任务监控页面,发现只有少量 reduce 子任务未完成,因为其处理的数据量和其他的 reduce 差异过大。单一 reduce 处理的记录数和平均记录数相差太大,通常达到好几倍之多,最长时间远大 于平均时长。
底层的原理--关于Map Reduce

案例:
若进行 word count 的文本有100G, 其中 80G 全部是 “aaa” ,剩下 20G 是其余单词,就会形成 80G 的数据量交给一个 reduce 进行相加,其余 20G 根据 key 不同分散到不同 reduce 进行相加的情况。如此就造成了数据倾斜,临床反应就是 reduce 跑到 99%然后一直在原地等着 那80G 的reduce 跑完。
主要原因:
hive执行底层转为MapReduce数据处理过程,数据经过 map后,由于不同key 的数据量分布不均,在shuffle 阶段中通过 partition 将相同的 key 的数据打上发往同一个 reducer 的标记,然后开始 spill (溢写)写入磁盘,最后merge成最终map阶段输出文件。
02
—
hive 哪些现象容易造成数据倾斜
1)hadoop框架特征:
A、不怕数据大,怕数据倾斜
B、Jobs 数比较多的作业运行效率相对比较低,如子查询比较多
C、 sum,count,max,min 等聚集函数,通常不会有数据倾斜问题
2)产生数据倾斜的原因
key分布不均匀;
业务数据本身的特性;
建表时考虑不周;
某些SQL语句本身就有数据倾斜;
3)产生数据倾斜的几种SQL
SQL中下列情况容易出现倾斜:
group by
group by 维度过小,某值的数量过多,造成处理某值的reduce非常耗时
去重 distinct count(distinct xx)
某特殊值过多, 处理此特殊值的reduce耗时
连接 join
这里有两种情形:
小表join大表,其中一个表较小,但是key集中,分发到某一个或几个Reduce上的数据远高于平均值;
大表与大表,但是分桶的判断字段0值或空值过多,这些空值都由一个reduce处理,非常慢
集中于以下使用情况:
A、group by 不和聚集函数搭配使用的时候
B、count(distinct),在数据量大的情况下,容易数据倾斜,因为 count(distinct)是按 group by 字段分组,按 distinct 字段排序
C、 小表关联超大表 join
03
—
hive数据倾斜业务场景及其解决方案
1)空值产生的数据倾斜
空值产生数据倾斜,主要在两个表关联的时候,字段出现空值而且还作为关联键;
在日志中,常会有信息丢失的问题,比如日志中的 user_id,如果取其中的 user_id 和用户表中的 user_id 相关联,就会碰到数据倾斜的问题。
把空值的 key 变成一个字符串加上一个随机数
解决方式一:
为空值的这部分 数据 union all 两个表关联的时候,where条件过滤关键键不为空值
select * from log a join user b on a.user_id is not null and a.user_id = b.user_id
union all
select * from log c where c.user_id is null;解决方式二:
当值为空的时候,用一个随机函数处理,
select * from log a left outer join user b on
case when a.user_id is null then concat('hive',rand()) else a.user_id end = b.user_id;效率比较:
方法二,log表读取一次,整个运行IO少,job数是1;效率更高方法一,log表读取两次,整个运行IO多,job数是2;效率更低2)不同数据类型关联产生数据倾斜用户表中 user_id 字段为 int,log 表中 user_id 为既有 string 也有 int 的类型, 当按照两个表的 user_id 进行 join 操作的时候,默认的 hash 操作会按照 int 类型的 id 进 行分配,这样就会导致所有的 string 类型的 id 就被分到同一个 reducer 当中把数字类型 id 转换成 string 类型的 idselect * from user a left outer join log b on b.user_id = cast(a.user_id as string)
3)大小表关联查询产生数据倾斜 注意:使用map join解决小表关联大表造成的数据倾斜问题。这个方法使用的频率很高。map join 概念:将其中做连接的小表(全量数据)分发到所有 MapTask 端进行 Join,从 而避免了 reduceTask,前提要求是内存足以装下该全量数据
怎么设置?
hive0.11 版本以前 ---要在写的SQL里面加mapjoinSELECT关键字后面添加/*+ MAPJOIN(tablelist) */提示优化器转化为map joinselect /* +mapjoin(a) */ a.id aid, name, age from a join b on a.id = b.id;
select /* +mapjoin(movies) */ a.title, b.rating from movies a join ratings b on a.movieid =
b.movieid;hive0.11 版本以后 ---开启参数即可set hive.auto.convert.join=true; 设置 MapJoin 优化自动开启
set hive.mapjoin.smalltable.filesize=25000000
小表join大表:开启mapjoin,设置参数大大表关联:把大表切分成小表,然后分别 map join
小表不大不小 join 大表:如果小表很大,大到 map join 会出现 bug 或异常,需要特殊处理比如:users 表有 600w+的记录,把 users 分发到所有的 map 上也是个不小的开销,而且 map join 不支持这么大的小表。如果用普通的 join,又会碰到数据倾斜的问题。改进方案:select /*+mapjoin(x)*/* from log a
left outer join (
select /*+mapjoin(c)*/ d.*
from ( select distinct user_id from log ) c join users d on c.user_id = d.user_id
) x
on a.user_id = x.user_id;假如,log 里 user_id 有上百万个,这就又回到原来 map join 问题。所幸,每日的会员 uv 不会太多,有交易的会员不会太多,有点击的会员不会太多,有佣金的会员不会太多等 等。所以这个方法能解决很多场景下的数据倾斜问题。
总结:这一节我们讲了关于HIVE数据倾斜的相关问题,要理解好这类问题,抓住核心就是MR底层原理,还有解决Key分布不均匀,集中于热点问题。下一节我们来总结下hive的其它问题,也是根据常见的面试问题点。
#我是媛姐,一枚有多年大数据经验的程序媛,打过螺丝搬过砖,关注数仓,关注分析。愿你我走得更远!



