一、效率低下的原因
1、数据倾斜
2、关联操作,job数太多
3、小文件太多,导致map性能差
数据倾斜是指,在数据量大的情况下,效率较低。比如count(distinct)是按group by 字段分组,按distinct字段排序,一般这种分布方式是很倾斜的。举个例子:比如男uv,女uv,像淘宝一天30亿的pv,如果按性别分组,分配2个reduce,每个reduce处理15亿数据。
有数据倾斜时进行负载均衡,set hive.groupby.skewindata,当选项设定为 true。 生成的查询计划有两 个 MapReduce 任务。在第一个 MapReduce 中,map 的输出结果集合会随机分布到 reduce 中, 每个 reduce 做部分聚合操作,并输出结果。这样处理的结果是,相同的 Group By Key 有可 能分发到不同的 reduce 中,从而达到负载均衡的目的;第二个 MapReduce 任务再根据预处 理的数据结果按照 Group By Key 分布到 reduce 中(这个过程可以保证相同的 Group By Key 分布到同一个 reduce 中),最后完成最终的聚合操作。
二、配置优化
1、数据裁剪,包括字段裁剪,分区裁剪,只查询需要的列和分区。
2、join 操作:
应该将条目少的表/子查询放在 Join 操作符的左边。 因为在 Reduce 阶段,位于 Join 操作符左边的表的内容会被加载进内存,载入条目较少的表 可以有效减少 OOM(out of memory)即内存溢出
如果 Join 的 key 相同,不管有多少个表,都会则会合并为一个 Map-Reduce
3、合理设置map大小
数据分割默认值是256M,假设input目录下有100个文件,大小都是10M以内,那么hadoop会分隔成100个块从而产生100个map数,map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。
set mapred.max.split.size=100000000;
set mapred.min.split.size.per.node=100000000;
set mapred.min.split.size.per.rack=100000000;
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
确定合并文件块的大小,大于文件块大小128m的,按照128m来分隔,小于128m,大于100m的,按照100m来分隔,把那些小于100m的(包括小文件和分隔大文件剩下的),进行合并
调整reduce个数
set mapred.reduce.tasks=15;
如下情况,不管你有没有调整reduce个数的参数,任务中一直都只有一个reduce任务
没有group by的汇总
用了Order by
有笛卡尔积
4、设置并行执行job
set hive.exec.parallel=true;
set hive.exec.parallel.thread.number=16; //同一个sql允许最大并行度,默认为8。
比如:如下sql ,四个map设置参数以后可以并行处理
insert overwrite table a partition(pt='0')
select c1,c2 where key='0'
insert overwrite table a partition(pt='1')
select c1,c2 where key='1'
insert overwrite table a partition(pt='2')
select c1,c2 where key='2'
insert overwrite table a partition(pt='3')
三、SQL优化
1、无效ID在关联时的数据倾斜问题
ID出现主键为 null 的情况,如果取其中key值关联,就会碰到数据倾斜的问题。原因是 Hive 中,主键为 null 值的项会被当做相同的 Key 而分配进同一个计算 Map。
处理方法: WHEN a.user_id IS NULL THEN CONCAT(‘dp_hive’,RAND()) ELSE a.user_id END =b.user_id;
2、不同数据类型关联产生的倾斜问题
如果关联表既有int类型又有string 类型,默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中。
把数据类型转换为统一的类型,再做关联
select * from users a join logs b on a.usr_id = cast(b.user_id as string)
3、GROUP BY替代COUNT(DISTINCT)达到优化效果
当数据量大的时候,用GROUP BY 替代COUNT(DISTINCT)解决数据倾斜问题,但是当数据量小的时候,反而效果不好。
统计每日IP
```
CREATE TABLE ip_2014_12_29 AS SELECT COUNT(DISTINCT ip) AS IP FROM logdfs WHERE logdate='2014_12_29';
```
由于引入了DISTINCT,因此在Map阶段无法利用combine对输出结果消重,必须将id作为Key输出,在Reduce阶段再对来自于不同Map Task、相同Key的结果进行消重,计入最终统计值。
我们看到作业运行时的Reduce Task个数为1,对于统计大数据量时,这会导致最终Map的全部输出由单个的ReduceTask处理。这唯一的Reduce Task需要Shuffle大量的数据,并且进行排序聚合等处理,这使得它成为整个作业的IO和运算瓶颈。
统计每日IP(改造)
```
CREATE TABLE ip_2014_12_29 AS SELECT COUNT(1) AS IP FROM (SELECT DISTINCT ip from logdfs WHERE logdate='2014_12_29') tmp;
```
我们利用Hive对嵌套语句的支持,将原来一个MapReduce作业转换为两个作业,在第一阶段选出全部的非重复id,在第二阶段再对这些已消重的id进行计数。这样在第一阶段我们可以通过增大Reduce的并发数,并发处理Map输出。在第二阶段,由于id已经消重,因此COUNT(*)操作在Map阶段不需要输出原id数据,只输出一个合并后的计数即可。
四、总结:
1. 不怕数据多,就怕数据倾斜。
2. 对jobs数比较多的作业运行效率相对比较低,比如即使有几百行的表,如果多次关联多次汇总,产生十几个jobs,没半小时是跑不完的。map reduce作业初始化的时间是比较长的。
3. 对sum,count来说,不存在数据倾斜问题。
4. 对count(distinct ),效率较低,数据量一多,准出问题,如果是多count(distinct )效率更低。