1)map side join
mapJoin的主要意思就是,当链接的两个表是一个比较小的表和一个特别大的表的时候,我们把比较小的table直接放到内存中去,然后再对比较大的表格进行map操作。join就发生在map操作的时候,每当扫描一个大的table中的数据,就要去去查看小表的数据,哪条与之相符,继而进行连接。这里的join并不会涉及reduce操作。map端join的优势就是在于没有shuffle,在实际的应用中,我们这样设置:
set hive.auto.convert.join=true;
此外,hive有一个参数:hive.mapjoin.smalltable.filesize,默认值是25mb(其中一个表大小小于25mb时,自动启用mapjoin)
要求:在hive做join时,要求小表在前(左)
2)join语句优化
优化前
select m.cid,u.id form order m join customer u on m.cid=u.id where m.dt=’20160801’;
优化后
select m.cid,u.id from (select cid from order where dt=’20160801’)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
优化后
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作业后执行示意图如下,图中还列出了我们实验作业中Reduce阶段的数据规模:
由于引入了DISTINCT,因此在Map阶段无法利用combine对输出结果消重,必须将id作为Key输出,在Reduce阶段再对来自于不同Map Task、相同Key的结果进行消重,计入最终统计值。
我们看到作业运行时的Reduce Task个数为1,对于统计大数据量时,这会导致最终Map的全部输出由单个的ReduceTask处理。这唯一的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对嵌套语句的支持,将原来一个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 … ) t;
这一优化使得在同样的运行环境下,优化后的语句执行只需要原语句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)关闭推测执行机制
因为在测试环境下我们都把应用程序跑通了,如果还加上推测执行,如果有一个数据分片本来就会发生数据倾斜,执行执行时间就是比其他的时间长,那么hive就会把这个执行时间长的job当作运行失败,继而又产生一个相同的job去运行,后果可想而知。可通过如下设置关闭推测执行:
set mapreduce.map.speculative=false
set mapreduce.reduce.speculative=false
set hive.mapred.reduce.tasks.speculative.execution=false