Hive解决数据倾斜问题及Hive优化

数据倾斜概述
简单来说数据倾斜就是数据的key的分化严重不均,造成一部分数据很多,一部分数据很少的情况。举个word count的入门例子,在map阶段形成了(“hello”,1)的形式,然后在reduce阶段进行value统计,算出"hello"出现的次数,假设word count的文本大小是100G,其中70G都是"hello",剩下的30G是其它单词,那就会形成70G的数据量交给一个reduce进行处理,其余30G根据key的不同分散到对应的reduce进行处理,这样的情况就会造成数据倾斜,结果就是其它reduce跑到99%以后一直在等待那个处理70G的reduce。
从另一角度看数据倾斜,其本质还是单台节点在执行那部分数据reduce的时候,由于数据量大,造成任务进度缓慢。若这台节点的机器内存和CPU、网络资源充足,处理100G和10M的数据所耗时间差不多,那也就不会存在其它reduce等待的现象,这点数据倾斜也掀不起什么风浪,但是如果数据量非常的大,TB/PB级的时候,一旦数据量远超机器的极限时就会造成reduce的缓慢甚至卡顿。日常工作中容易造成数据倾斜的原因可以归纳为如下几点:
1.group by
2.distinct count(distinct xx)
3. join
group by 的数据倾斜处理
调优参数:
hive.map.aggr=true;
set hive.groupby.skewindata=true;
原理:hive.map.aggr=true 这个配置项代表是否在map端进行聚合
set hive.groupby.skewindata=true;数据倾斜时负载均衡,当设定为true,生成的查询计划会有两个MRJob,第一个Job中Map的输出结果集合会随机分布到Reduce中,每个reduce做部分聚合处理,并输出结果,这样处理的结果是相同groupby key有可能被分布到不同的reduce中,从而达到负载均衡的目的。第二个MRJob再根据预处理的结果按照GroupBy key 分布到reduce中(这个过程可以保证相同的GroupBy key 被分布到同一个reduce中),完成最终的聚合操作,因此基本可以解决数据倾斜问题。
Hive优化
1.map side join
MapJoin的意思就是当链接的两个表是一个比较小的表,一个是比较大的表的时候,把比较小的表直接放到内存中去,然后再对比较大的表进行Map操作,join就会在map操作的时候,每当扫描一个大的table中的数据,就要去查看小表的数据,哪条与之相符,继而进行连接。 这里的join并不会涉及reduce操作。map端join的优势就是在于没有shuffle,在实际的应用中,我们这样设置:
set hive.auto.convert.join=true; //将小表加载到内存中
此外hive有一个参数 hive.mapjoin.smalltable.filesize 默认值是25mb(其中一个表大小小于25m时,自动开启mapjoin)。
注意:在hive做join时,要求小表在前(左)。
2.join 语句优化
优化前:select m.cid,u.id from order m join customer u on m.cid=u.id where m.dt=‘20181220’;
优化后:select m.cid,u.id from (select cid from order where dt=‘20181220’)m join customer u on m.cid = u.id;
注意;hive在做join时小表写在前面。
3.group by优化
hive.groupby.skewindata=true;
如果group by 过程出现数据倾斜,应该设置为true
4.count distinct 优化
优化前:select count(distinct id) from tablename;
注意:count操作是全局计数,在底层转换MRJob时用于计数的分区(reduce Task)只有一个。
优化后:select count(*) from (select distinct id from tablename) tmp;
此外再设定一下reduce任务数量
注意:count这种全局计数操作hive只会用一个reduce来实现。
日常统计场景中我们经常会对一段时期内的字段进行去重并统计数量,SQL语句类似于:select count(distinct id) from table_name where…; 这条语句是从一个表符合where条件的记录中统计不重复的id总数,此语句转化为MapReduce后执行示意图如下在这里插入图片描述
由于引入了distinct,因此在Map阶段无法用combine对输出的结果去重,必须将id作为key输出,在Reduce阶段再对来自于不同Map task 、相同key的结果进行去重,计入最终统计值。我们看到运行的Reduce Task 个数为1 ,对于统计大量数据时会导致最终Map的全部输出由单个Reduce Task处理,这唯一 的Reduce Task需要Shuffle大量的数据,并且进行排序聚合等处理,这使得它成为整个作业的IO的运算瓶颈。
经过上述分析后,我们尝试显式地增大Reduce Task个数来提高Reduce阶段的并发,使每一个Reduce Task的数据处理量控制在2G左右。具体设置如下:
set mapred.reduce.tasks=100
调整后我们发现这一参数并没有影响实际Reduce Task个数,Hive运行时输出
“Number of reduce tasks determined at compile time: 1”。
原因是Hive在处理count这种“全聚合(full aggregates)”计算时,它会忽略用户指定的Reduce Task数,而强制使用1。
所以我们只能采用变通的方法来绕过这一限制。我们利用Hive对嵌套语句的支持,将原分区 hive02 的第 8 页所以我们只能采用变通的方法来绕过这一限制。我们利用Hive对嵌套语句的支持,将原来一个MapReduce作业转换为两个作业,在第一阶段选出全部的非重复id,在第二阶段再对这些已消重的id进行计数。这样在第一阶段我们可以通过增大Reduce的并发数,并发处理Map输出。在第二阶段,由于id已经消重,因此count()操作在Map阶段不需要输出原id数据,只输出一个合并后的计数即可。这样即使第二阶段Hive强制指
定一个Reduce Task,极少量的Map输出数据也不会使单一的Reduce Task成为瓶颈。
改进后的SQL语句如下:
**select count(
) from (select distinct id from table_name where …);**
这一优化使得在同样的运行环境下,优化后的语句执行只需要原语句20%左右的时间,优化后的MapReduce作业流程如下:在这里插入图片描述
5.调整切片数(Map任务数)
Hive底层自动对小文件做了优化,用了CombineTextInputFormat,将多个小文件切片合成一个切片。合成完之后的切片大小,如果>mapred.max.split.size 的大小,就会生成一个新的切片。
mapred.max.split.size 默认是128MB
**set mapred.max.split.size=134217728 ** //(128MB)
对于切片数(MapTask)数量的调整,要根据实际业务来定,比如一个100MB的文件,假设有1千万条数据,此时可以调成10个MapTask,则每个MapTask处理1百万条数据。
6.JVM重利用
set mapred.job.reuse.jvm.num.tasks=20(默认是1个)
JVM重用是hadoop调优参数的内容,对hive的性能具有非常大的影响,特别是对于很难避免小文件的场景或者task特别多的场景,这类场景大多数执行时间都很短。这时JVM的启动过程可能会造成相当大的开销,尤其是执行的job包含有成千上万个task任务的情况。
JVM重用可以使得一个JVM进程在同一个JOB中重新使用N次后才会销毁。
7.启用严格模式
在hive里面可以通过严格模式防止用户执行那些可能产生意想不到的查询,从而保护hive的集群。
可以通过 set hive.mapred.mode=strict 来设置严格模式,改成unstrict则为非严格模式。
在严格模式下,用户在运行如下query的时候会报错:
①分区表的查询没有使用分区字段来限制
②使用了order by 但没有使用limit语句。(如果不使用limit,会对查询结果进行全局排序,消耗时间长)
③产生了笛卡尔积
当用户写代码将表的别名写错的时候会引起笛卡尔积,例如
select * from origindb.promotion__campaign c JOIN origindb.promotion__campaignex ce ON c.id = c.id limit 1000;
8.关闭推测执行机制
因为在测试环境下我们都把应用程序测试OK了,如果还加上推测执行,如果有一个数据分片本来就会发生数据倾斜,执行时间就是比其他的时间长,那么hive就会把这个执行时间长的job当作运行失败,继而又产生一个相同的job去运行,后果可想而知,可通过如下设置关闭推测执行:
set mapreduce.map.speculative=false
set mapreduce.reduce.speculative=false
set hive.mapred.reduce.tasks.speculative.execution=false

猜你喜欢

转载自blog.csdn.net/weixin_43519014/article/details/85122813