一、原因:

1.key的null和空值数量过多,这些都被分到一个reduce中去处理,导致某一个或几个reduce的任务要远大于平均值

2.key的值分布不均匀,某一个或者多个key相同的数据量过大(有些情况是由业务本身的特性决定的)

3.SQL语句不合理

二、现象:

在运行日志里面可以明显看出,任务执行进度一直维持在99%,由于某一个或几个reduce处理的数据量过大且资源无法平均,所以会大大拖慢查询速度。

三、解决方法:

1.开启预聚合MR

hive.map.aggr = true

hive.groupby.skewindata=true

原理:这个就相当于在跑SQL的时候开启了两个MR,第一个MR会先把聚合后的结果随机分配到reduce中,在reduce中先做部分聚合,然后将输出结果输入到第二个MR,然后将相同的key分发到相同的reduce中进行聚合,但是这次已经是提前聚合果一次的了,所以数量会减少

2.join的优化

①在使用某个表之前,先进行有效条件过滤,同时尽量避免使用select *,这样做是为了尽可能的减少参与join的数据条数

②使用/*+mapjoin(x)*/,在小表join大表的时候,可以将小表放到内存里,这样做是为了避免给所有的map都分发一遍数据,例如:select userid from a left join b,a是小表,可以写为select /*+mapjoin(a)*/ userid from a left join b。

注意===0.11版本后mapjoin自动开启,hive.auto.convert.join默认值为true,hive.mapjoin.smalltable.filesize默认值为2500000(25M)。如果出现mapjoin不起作用的时候,可以手动关闭hive.auto.convert.join=false(关闭自动MAPJOIN转换操作)
hive.ignore.mapjoin.hint=false(不忽略MAPJOIN标记)这两个参数,以此来关闭默认的mapjoin,使用sql中后来配置的。

③在进行join的时候可以尽量来做到空值和null值得过滤,前提是如果这些空值和null值在开放中是没有用的数据

④尽量使用gruop by 来代替 distinct

⑤正常表关联,可以先给左表的id为null的加上一个字符串+随机数,避免在关联时,左表的所有null值都进入了一个reduce,例如:select * from log a left outer join users b on a.uid = b.uid,但是a.uid非常多的值为null,那么就需要改为select * from log a left outer join users b on case when a.uid is null then concat(‘自定义字符串’,rand() ) else a.uid end = b.uid,这样可以避免所有的null值进入一个reduce。

3.通过条件(例如时间,范围等字段)来做限制,分若干部分来跑,最后再进行union操作

4.通过对SQL逻辑的改变来调节数据关联,输入输出的方式,以此来规避数据倾斜的出现

解决方法后续补充:

1、set hive.optimize.skewjoin=true; //有数据倾斜时开启负载均衡,默认 false