本文从hql 语句和参数设置两个方面入手,介绍一些可以通用的优化策略。至于map 和reduce 数量的调整暂不叙述。
文章目录
配置文件修改:
在当前用户的home目录下创建文件
vim $HOME/.hiverc
hive在每次启动时都会加载文件中的内容使配置生效。
如果希望所有用户都启用配置,可将配置项增加到
$HIVE_HOME/conf/hive-site.xml
一、join优化
1. 小表连大表
hive会假定查询中最后一张表是最大的表,在对每行记录进行连接操作时,会把其他表缓存起来,扫描最后一张表进行计算,因此应该按照表大小从左到右进行连接
2. join 相同条件放在一起
多表连接时,如果每个on 子句都用到同一个连接键,可以在同一个MapReduce job中连接多张表
如下查询都用到A.a作为连接键,以在同一个MapReduce job中连接3张表
select A.a, A.b, B.b, C.b
from A
join B on A.a = B.a
join C on A.a = C.a
3. mapjoin
如果只有一张表是小表,可以在最大的表通过mapper的时候将小表完全放到内存中,在map 端执行连接过程,省略掉reduce 过程
set hive.auto.convert.join = true ;
可以通过配置使用这个优化的小表的字节大小
hive.mapjoin.smalltable.filesize = 25000000
右连接和全连接不支持此优化
4. 桶表连接优化
当所有表中的数据都按照on 语句中的连接键进行分桶,且其中一张表中的分桶数是另一张表的倍数,这时hive 可以在map 阶段按分桶数据进行连接,而不需要获取所有数据
set hive.optimize.bucketmapjoin = true
如果所有的分桶表都具有相同的分桶数,而且表中的数据已经按连接键排序过,可以执行分类-合并连接(sort-merge join)的优化,限制条件比较复杂很少用到
set hive.input.format = org.apache.hadoop.hive.ql.io.bucketizedHiveInputFormat;
set hive.optimize.bucketmapjoin = true;
set hive.optimize.bucketmapjoin.sortedmerge = true;
5. 使用left-semi join
使用left-semi join代替inner join,对于左表中指定的一条数据,右表中一旦找到匹配的记录,便会停止扫描
6. 谓词下推
Predicate Pushdown(PPD)
谓词下推概念中的谓词指返回bool值即true和false的函数,或是隐式转换为bool的函数。下推指把过滤条件下推到map端,提前执行过滤,以减少map到reduce的传输数据,提升整体性能。
即先过滤再join,通过将一些过滤条件尽可能的在最底层执行,可以减少每一层交互的数据量,从而提升性能。
set hive.optimize.ppd = true
谓词下推的规则:
inner join时,谓词任意放都会下推
left join时,左表的谓词应该写在where后,右表的谓词应写在on后
right join时,左表的谓词应该写在on后,右表的谓词应写在where后
full join时,都不会下推
以left join为例:
会下推优化:
select count(1) from A left join B on A.a = B.a where A.b='c';
select count(1) from A left join B on ( A.a = B.a and B.b='c');
不会下推优化:
select count(1) from A left join B on (A.a = B.a and A.b='c');
select count(1) from A left join B on A.a = B.a where B.b='c';
7. join 字段显式类型转换
当参与join 的字段类型不一致时,hive 会自动进行类型转换,但是自动转换有时候效率并不高,可以根据实际情况通过显式类型转换cast() 函数来避免hive的自动转换。
二、group by优化
1. 数据倾斜
在MapReduce程序中,同一个分组的数据会分配到同一个reduce 操作上去,导致某一些reduce 压力很大,其他的reduce 压力很小,这就是数据倾斜,整个job 执行时间取决于那个执行最慢的reduce。
set hive.groupby.skewindata = true
当选项设定为true,生成的查询计划会有两个MapReduce Job。
第一个job 中,map 的输出结果会随机分布到reduce 中,每个reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的Group By Key有可能被分发到不同的reduce 中,从而达到负载均衡的目的;
第二个job再根据预处理的数据结果按照Group By Key分布到reduce 中,完成最终的聚合操作。
2. map 端聚合
并不是所有的聚合操作都需要在reduce 端完成,可以先在map 端进行部分聚合,最后在reduce 端得出最终结果。
set hive.map.aggr = true
map端进行聚合操作的条目数目
set hive.groupby.mapaggr.checkinterval = 100000
三、order by优化
由于是全局排序,order by的操作要在map 后所有的数据都汇集到一个reduce 上执行,导致一个reduce 处理大量数据,如果数据量大执行时间会很久。
在要有进行order by全局排序的需求时:
1.尽量在结果集上排序,避免在中间的大数据集上order by操作;
2.如果需求是取排序后前n 条数据,可以使用distribute by和sort by在各个reduce 上进行排序后取前n条,然后再对各个reduce 的结果集合并后在一个reduce 中全局排序,再取前n条
如:
select id, name from tb_name order by length(name) desc limit 10 ;
可以改写为
select a.id,a.name from
(
select id,name from tb_name
distribute by length(name) sort by length(name) desc limit 10
) a
order by length(a.name) desc limit 10;
四、本地模式
在数据量非常小的情况下,集群触发执行任务的时间可能比实际任务执行的时间更久,这时可以启动本地模式在单台机器上处理所有任务
set hive.exec.mode.local.auto = true ;
五、读取数据不启用MapReduce
启用MapReduce Job是会消耗系统开销的,对于简单的不需要聚合的类似
select col from tb_name limit n ;
不需要起MapReduce job,hive 可以通过直接抓取的方式读取对应表存储目录下的文件,然后输出查询结果到控制台。
set hive.fetch.task.conversion = more ;
- minimal:只有 select * 、在分区字段上where 过滤、有limit 这三种场景下才启用直接抓取方式
- more:在select、 where 筛选、limit 时,都启用直接抓取方式,建议启用more
六、JVM重用
默认情况下,MapReduce 中一个Map Task或者一个Reduce Task就会启动一个JVM 进程,一个Task 执行完毕后,JVM 进程就退出。 这样如果任务花费时间很短,又要多次启动JVM 的情况下,JVM 的启动时间会变成一个比较大的消耗,这个时候就可以通过重用JVM 来解决。
set mapred.job.reuse.jvm.num.tasks = 3 ; #指定一个JVM 进程在运行几次任务后再退出
七、设置并行
hive会将一个查询转化成多个阶段执行,默认情况下一次只会执行一个阶段,但有些阶段并非相互依赖,可以并行执行,将整个执行时间缩短
set hive.exec.parallel = true ;
set hive.exec.parallel.thread.number = 4 ; #同一条查询允许的最大并行量
八、开启严格模式
set hive.mapred.mode=strict;
hive严格模式有三个限制:
- 分区表必须在where条件限制分区
- order by语句必须加limit语句
- 禁止笛卡尔积连接
九、小文件优化
由于每个小文件都会启动一个map 任务,如果文件过小,以至于map 任务启动和初始化的时间大于逻辑处理的时间,会造成资源浪费,甚至OOM。可以在MapReduce处理的过程对小文件进行合并以及在hadoop中进行归档。
输入合并
set mapred.max.split.size = 256000000; #每个Map最大输入大小,合并文件的数量由此决定
set mapred.min.split.size.per.node = 100000000; #一个节点上split的至少的大小
set mapred.min.split.size.per.rack = 100000000; #一个交换机下split的至少的大小
set hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; #执行Map前进行小文件合并
输出合并
set hive.merge.mapfiles = true; #在Map-only的任务结束时合并小文件
set hive.merge.mapredfiles = true; #在MapReduce的任务结束时合并小文件
set hive.merge.size.per.task = 256000000; #合并文件的大小
set hive.merge.smallfiles.avgsize=16000000; #当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge
Hadoop归档文件也是解决小文件问题的方式之一:
set hive.archive.enabled = true ; #启用数据归档
set hive.archive.har.parentdir.settable = true ;
set har.partfile.size=1099511627776; #归档后文件大小
十、建表优化
数据量大建表的时候合理选择分区表和桶表;
尽量采用列式存储,如ORC格式不仅读取性能高还具有压缩效果
十一、一次读取多次插入
有些场景是从一个表读取数据后,要多次利用,这时候就可以使用from … insert into … 语法,将from 前置,只遍历一次hive 表,避免多次读取
如:
from tb_name1
insert overwrite table log1 select a,b,c where d='1'
insert overwrite table log1 select a,b,c where d='2'