hive作为数仓工程师必不可缺少的计算组件,在工作中经常遇到一些调优问题,本篇文章是hive性能调优详解系列之第二篇hive语法层面调优。上一篇可点击
hive建表设计层面调优
一.hive语法层面和参数调优
hive语法和参数调优将是hive调优一大重点,并能间接解决数据倾斜问题,同事提升运行效率也是重中之重。
1.1查看hive执行计划
Hive的SQL语句在执行之前需要将SQL语句转换成MapReduce任务,因此需要了解具体的转换过 程,可以在SQL语句中输入如下命令查看具体的执行计划。
##查看执行计划,添加extended关键字可以查看更加详细的执行计划 explain [extended] query
我们不仅能通过执行计划获取我们想要看到的参数,而且还能检测sql语法正确与否。
1.2.列裁剪
列裁剪就是在查询时只读取需要的列,分区裁剪就是只读取需要的分区。当列很多或者数据量很大时, 如果select * 或者前旨定分区,全列扫描和全表扫描效率都很低Hive在读数据的时候,可以只读取查询中所需要用到的列,而忽略其他的列。这样做可以节省读取开销:中间表存储开销和数据整合开销。
set hive, optimize, cp = true; ##列裁如,取数只取查询中需要用到的列,默认是true
1.3.谓词下推
将SQL语句中的where谓词逻辑都尽可能提前执行,减少下游处理的数据量。对应逻辑优化器是 PredicatePush Downo
set hive. optimize. ppd=true; ## 默认是true
示例程序:
seiect a.*, b.* from a join b on a.id = b.id where b.age > 30;
seiect a. *, c. * from a join (seiect * from b where age > 30) c on a. id = c. id;
1.4.分区裁剪
列裁剪就是在查询时只读取需要的列,分区裁剪就是只读取需要的分区。当列很多或者数据量很大时, 如果select *或者不指定分区,全列扫描和全表扫描效率都很低。
在查询的过程中只选择需要的分区,可以减少读入的分区数目,减少读入的数据量。
Hive中与分区裁剪优化相关的则是:
set hive.optimize.pruner=true; ## 默认是true
在HiveQL解析阶段对应的则是ColumnPruner逻辑优化器。
select * from student where department = “aaaa”;
1.5.合并小文件
我们都知道如果一个mapreduce job碰到一对小文件作为输入,一个小文件启动一个Task,这个本身对资源是极度浪费
Map输入合并
将输 入的小文件进行合并,从而减少mapTask任务数量。
Map端输入、合并文件之后按照block的大小分割(默认) set
hive.input.format=org.apache.hadoop.hive.ql.io.combineHivelnputFormat;
Map端输入,不合并 set
hive.input.format=org.apache.hadoop.hive.ql.io.HivelnputFormat;
Map/Reduce输出合并
大量的小文件会给HDFS带来压力,影响处哩效率。可以通过合并Map和Reduce的结果文件来消除影响。
##是否合并Map输出文件,!默认值为true set hive.merge.mapfiles=true;
##是否合并Reduce端输出文件,默认值为false set hive.merge.mapredfiles=true;
##合并文件大小,默认值为256000000 set hive.merge.size.per.task=256000000;
##每个Map最大分割大小 set mapred.max.spint.size=256000000; 一个节点上split的最少值 set map red .min. spl it.size.per. node=1; // 服务器节点 一个机架上split的最少值 set
mapred.min. split.size.per. rack=1; // 服务器机架
hive.merge.size.per.task 和 mapred.min.split.size.per.node 联合起来:
1.默认情况先把这个节点上的所有数据进行合并,如果合并的那个文件的大小超过了256M就开启另外一个文件继续合并
2.如果当前这个节点上的数据不足256M,那么就都合并成一个逻辑切片
现在有100个Task,总共有10000M的数据,平均一下,每个Task执行100M的数据的计算。
假设只启动10个Task,每个Task就要执行1000M的数据。如果只有2个Task, 5000M
1.6.合理设置MapTask井行度
一.MapReduce中的MapTask的并行度机制
Map数过大:当输入文件特别大,MapTask特别多,每个计算节点分配执行的MapTask都很多,这 时候可以考虑减少MapTask的数量。
增大每个MapTask处理的数据量。而且MapTask过多,最终生成的结果文件数也太多,所以面临两个问题
1.Map阶段输出文件太小,产生大量小文件
2.初始化和创建Map的开销很大
Map数太小:当输入文件都很大,任务逻辑复杂,MapTask执行非常慢的时候,可以考虑增加 MapTask数,来使得每个MapTask处理的数据量减少,从而提高任务的执行效率。
1.文件处理或查询并发度小,Job执行时间过长
2.大量作业时,容易堵塞集群
在MapReduce的编程案例中,我们得知,一个MapReduce Job的MapTask数量是由输入分片 Inputsplit决定的。而输入分片是由FilelnputFormat.getSplit()决定的。一仆俞入分片对应一个 MapTask,而输入分片是由三个参数决定的:
参数 | 默认值 | 意义 |
---|---|---|
dfs.blocksize | 128M | HDFS默认块大小 |
mapreduce.input.fileinputformat.split.minsize | 1 | 最小分片大小(MR) |
mapreduce.input.fileinputformat.split.maxsize | 256M | 最大分片大小(MR) |
输入分片大小的计算是这么计算出来的:
long splitsize = Math.max(minsize, Math.min(maxsize, blocksize))
默认情况下,输入分片大小和HDFS集群默认数据块大小一致,也就是默认一个数据块,启用一个 MapTask进行处理,这样做的好处是避免了服务器节点之间的数据传输,提高job处理效率
两种经典的控制MapTask的个数方案:减少MapTask数或者增加MapTask数
1.减少MapTask数是通过合并小文件来实现,这一点主要是针对数据源
2.增加MapTask数可以通过控制上一个job的reduceTask个数
重点注意:不推荐把这个值进行随意设置!
推荐的方式:使用默认的切块大小即可。如果非要调整,最好是切块的N倍数
NodeManager节点个数:N ===》Task = ( N * 0.95) * M
二.合理控制MapTask数量
1.减少MapTask数可以通过合并小文件来实现
2.增加MapTask数可以通id控制上一个ReduceTask默认的MapTask个数
计算方式
输入文件总大小:total_size
HDFS设置的数据块大小:dfs_block_size
default_mapper_num = total_size / dfs_block_size
MapReduce中提供了如下参数来控制map任务个数,从字面上看,貌似是可以直接设置MapTask个数的样子,但是很遗憾不行,这个参数设置只有在大于default_mapper_num 的时候,才会生效。
set mapred. map.tasks=10; ## 默认值是2
那如果我们需要减少MapTask数量,但是文件大小是固定的,那该怎么办呢?
可以通过
mapred.min.split.size
设置每个任务处理的文件的大小,这个大小只有在大于
dfs_block_size
的时候才会生效
split_size = max(mapred.min.split.size, dfs_block_size) split_num =
total_size / split_size compute_map_num = Math.min(split_num,
Math.max(default_mapper_num, mapred.map.tasks))
这样就可以减少MapTask数量了。
总结一下控制mapper个数的方法:
1.如果想增加MapTask个数,可以设置mapred.map.tasks为一个较大的值
2.如果想减少MapTask个数,可以设置maperd.min.split.size为一个较大的值
3.如果输入是大量小文件,想减少mapper个数,可以通过设置hive.input.format合并小文件
如果想要调整mapper个数,在调整之前,需要确定处理的文件大概大小以及文件的存在形式(是大量 小文件,建单个大文件),然后再设置合适的参数。不能盲目进行暴力设置,不然固导其反。
1.7.合理设置ReduceTask井行度
ReduceTask数量过多
一个ReduceTask会产生一个结果文件,这样就会生成很多小文件,那么 如果这些结果文件会作为下一个Job的输入,则会出现小文件需要进行合并的问题,而且启动和初始化 ReduceTask需要耗费资源。
ReduceTask数量过少
一个ReduceTask就需要处理大量的数据,并且还有可能会出现数据 倾斜的问题,使得整个查询耗时长。
设置参数
默认情况下,Hive分配的reducer个数由下列参数决定:
Hive会猜测确定一个ReducerTask个数,基于以下两个设定:
参数1:hive. exec.reducers, bytes.per. reducer (默认256m)
参数2: hive. exec.reducers.max (默认为1009)
参数3: mapreduce. job.reduces (默认值为-1表示没有设置,那么就按照以上两个参数进行设置)
ReduceTask的计算公式为:
N = Math.min(参数2,总输入数据大小/参数1)
可以通过改变上述两个参数的值来控制ReduceTask的数量。也可以通过
set mapred.map.tasks=10;
set mapreduce.job.reduces=10;
通常情况下,有必要手动指定ReduceTask个数。
考虑到Mapper阶段的输出数据量通常会比输入有大幅减少,因此即使不设定ReduceTask个数,重设参数2 必要的。
**老姜经验:**默认之后出现小文件,可减少,文件普遍在hdfs块默认即可。如果执行效率比较慢,可以再提升,控制文件大小在默认块60%-80%即可。
1.8. Join 优化
Join优化整体原则:
1.优先过滤后再进行Join操作,最大限度的减少参与join的数据量
2.小表join大表,最好启动map join, hive自动启用mapjoin,小表不能超过25M,可以更改
3.Join on的条件相同的话,最好放入同一个job,并且join表的排列顺序从小到大:
select a.*,b. *, c. * from a join b on a. id = b. id join c on a. id = c. i
4.如果多张表做join,如果多个链接条件都相同,会转换成一个job
优先过滤数据
尽量减少每个阶段的数据量,对于分区表能用上分区字段的尽量使用,同时只选择后面需要使用到的列,最大 限度的减少参与Join的数据量。
小表join大表原则
小表join大表的时应遵守小表join大表原则,原因是join操作的reduce阶段,位于join左边
的表内容会被加载进内存,将条目少的表放在左边,可以有效减少发生内存溢出的几率。join中执行顺序是
从左到右生成Job,应该保证连续查询中的表的大小从左到右是依次增加的。
使用相同的连接键
在hive中,当对3个或更多张表进行join时,如果on条件使用相同字段,那么它们会合并为一个 MapReduce
Job,利用这种特性,可以将相同的join on放入一个job来节省执行时间。
尽量原子操作
尽量避免一个SQL包含复杂的逻辑,可以使用中间表来完成复杂的逻辑。
大表Join大表
1.空key过滤:有时join超时是因为某些key对应的数据太多,而相同key对应的数据都会发送到相同的 reducer上,从而导致内存不够。此时我们应该仔细分析这些异常的key,很多情况下,这些key对应的数据
是异常数据,我们需要在SQL语句中进行过滤。
2.空key转换:有时虽然某个key为空对应的数据很多,但是相应的数据不是异常数据,必须要包含在join 的结果中,此时我们可以表a中key为空的字段赋一个随机的值,使得数据随机均匀地分不到不同的reducer 上
1.9.启用 Mapjoin
Mapjoin是将join双方比较小的表直接分发至各自map进程的内存中,在map进程中进行join操作,这样就不用进行reduce步骤,从而提高了速度。
只有join操作才能启用Mapjoin
。
##是否根据输入小表的大小,自动将reduce端的common join转化为map join,将小表刷入内存中。
##对应逻辑优化器是MapJoinProcessor set hive.auto.convert.join = true;
##刷入内存表大小(字节) set hive.mapjoi n.smalltable.filesize = 25000000; hive会基于表的size自动的将普通joi成mapjoin set
hive.auto.convert.join.noconditionaltask=true;
##多大的表可以自动触发到内层localtask,默认大小:100M set hive.auto.convert.join.noconditionaltask.size=10000000;
Hive可以进行多表Join。Join操作尤其是Join大表的时候代价是非常大的。Mapjoin特别适合大小表 join的情况。在Hive join场景中,一般总有一张相对小的表和一张相对大的表,小表叫build table,大表叫probe table.
Hive在解析带join的SQL语句时,会默认将最后一个表作为probe table,将前面的表作为build table并试图将它们读进内存。如果表顺序写反,probe table在前面,引发OOM的风险就高了。
在维度建模数据仓库中,事实表就是probe table,维度表就是build table。这种Join方式 在map端完成join过程,消灭了 reduce,效率很高。而且Mapjoin还支持非等值连接。
当Hive执行Join时,需要知道哪个表被流式传输(stream),哪个表被缓存(cache)。Hive将 JOIN语句中的最后一个表用于流式传输,因此我们需要确保这个流表在两者之间是最大的。如果要在不同的key上join更多的表,那么对于每个join集,只需在ON条件右侧指定较大的表。
也可以手动开启mapjoin:
–SQL方式,在SQL语句中添加mapjoin标记(mapjoin hint) 一将小表放到内存中,省去shffle操作
//在没有开启mapjoin的情况下,执行的是reduceJoin
SELECT /
+ MAPJOIN(smallTable)
/
smallTable.key, bigTable.value FROM smallTable JOIN bigTable ON
smallTable.key = bigTable.key;
/
+mapjoi n(smal1table)
/
Sort-Merge-Bucket(SM B) Map Join
它是另一种Hive Join优化技术,使用这个技术的前提是所有的表都必须是分桶表(bucket)和分桶排序 的(sort)。分桶表的优化!
具体实现:
1.针对参与join的这两张做相同的hash散列,每个桶里面的数据还要排序
2.这两张表的分桶个数要成倍数。
3.开启SMB join的开关
一些常见参数设置:
##当用户执行bucket map join的时候,发现不能执行时,禁止查询
set hive.enforce.sortmergebucketmapjoin=false;
##如果join的表通过sort merge join的条件,join是否转换为sort merge join
set hive.auto.convert.sortmerge.join=true;
##当两个分桶表join时,如果join on的是分桶字段,小表的分桶数是大表的倍数时,可以启用 map join来提高效率。 bucket map join优化,默认值是 false set hive.optimize.bucketmapjoin=false;
bucket map joibn 优化,默认值是 false set
hive.optimize.bucketmapjoin.sortedmerge=false;
1.10. Join数据倾斜优化
在编写Join查询语句时,如果确定是由于join出现的数据倾斜,那么请做如下设置:
join的键对应的记录条数超过这个值则会进行分拆,值根据具体数据量设置 set hive.skewjoin.key=100000;
#如果是join过程出现倾斜,如下参数设置为true set hive.optimize.skewjoin=false;
如果开启了,在join过程中Hive会将计数超过阈值hive.skewjoin.key (默认100000)的倾斜key对 应的行临时写进文件中,然后再启动另一个job做map join生成结果。
通过hive. skewjoin.mapjoin.map.tasks参数还可以控制第二个job的mapper数量,默认 10000。
set hive.skewjoi n.mapjoi n.map.tasks=10000;
1.11. CBO 优化
众所周知join的时候表的顺序的关系:前面的表都会被加载到内存中。后面的表进行磁盘扫描
select a. *, b. *, c. * from a join b on a. id = b. 1 d join c on a. id = c. id;
Hive自0.14.0开始,加入了一项”Cost based Optimizer”来对HQL执行计划进行优化,这个功能通 过”hive.cbo.enable”来开启。在Hive 1.1.0之后,这个feature是默认开启的,它可以自动优化HQL 中多个Join的顺序,并选择合适的Join算法。
**CBO,成本优化器,**代价最小的执行计划就是最好的执行计划。传统的数据库,成本优化器做出最优化的执行计划是按统计信息来计算的。Hive的成本优化器也一样。
Hive在提供最终执行之前,优化每个查询的执行逻辑和物理执行计划。这些优化工作是交给底层完成的。根据查询成本执行进一步的优化,从而产生潜在的不同决策:如何排序连接,执行哪种类型的连接,并行度等等。
要使用基于成本的优化(也称为CBO),请在查询开始设置以下参数:
set hive.cbo.enable=true;
set hive.compute.query.usi ng.stats=true;
set hive.stats.fetch.column.stats=true;
set hive.stats.fetch.partition.stats=true;
1.12.怎样做笛卡尔积
当Hive设定为严格模式(hive.mapred.mode=strict)时,不允许在HQL语句中出现笛卡尔积,这实际说明了 Hive对笛卡尔积支持较弱。因为找不到join key, hive只能使用1个reducer来完成笛卡尔积。
当然也可以使用limit的办法来减少某个表参与join的数据量,但对于需要笛卡尔积语义的需求来说, 经常是一个大表和一个小表的Join操作,结果仍然很大(以至于无法用单机处理),这时Mapjoin才是最优方案。
Mapjoin,顾名思义,会在Map端完成Join操作。这需要将Join操作的一个或多个表完全读入内存。
PS: Mapjoin在子查询中可能出现未知BUG。在大表和小表做笛卡尔积时,规避笛卡尔积的方法是, 给Join添加一个Join key,原理很简单:
将小表扩充一列join key
,并将小表的条目复制数倍,join key各不相同;将大表扩充一列join key为随机数。
数据复制几倍,最后就有几个reduce来做,而且大表的数据是前面小表扩张key值范围里面随机出来的,所以复制了几倍n,就相当于这个随机范围就有多大n,那么相应的,大表的数据就被随机的分为了 n份。并且最后处理所用的reduce数量也是n,而且也不会出现数据倾斜。
1.13. Group By优化
默认情况下,Map阶段同1个Key的数据会分发到一个Reduce上,当1个Key的数据过大时会产生数据倾斜。进行group by操作时可以从以下两个方面进行优化:
1.Map端部分聚合
事实上并不是所有的聚合操作都需要在Reduce部分进行,很多聚合操作都可以先在Map端进行部分 聚合,然后在Reduce端的得出最终结果。
##开启Map端聚合参数设置 set hive.map.aggr=true;
#设置map端预聚合的行数阈值,超过该值就分拆job,默认值100000 set hive.groupby.mapaggr.checkinterval=100000
2.有数据倾斜时进行负载均衡
当HQL语句使用groupby时数据出现倾斜时,如果该变量设置为true,那么Hive会自动进行负载均衡。策略就是把MapReduce任务拆分成两个:第一个先做预汇总,第二个再做最终汇总。
#自动优化,有数据倾斜的时候进行负载均衡(默认是false) set hive.groupby.skewindata=false;
当选项设定为true时,生成的查询计划有两个MapReduce任务。
1.在第一个MapReduce任务中,map的输出结果会随机分布到reduce中,每个reduce做部分聚合操作,并输出结果,这样处理的结果是相同的group
by key’有可能分发到不同的reduce中,从而达到 负载均衡的目的;
2.第二个MapReduce任务再根据预处理的数据结果按照group by key分布到各个reduce中,最后完成最终的聚合操作。
Map端部分聚合:并不是所有的聚合操作都需要在Reduce端完成,很多聚合操作都可以先在Map端进行部分聚合,最后在Reduce端得出最终结果,对应的优化器为GroupByOptimizer。
那么如何用group by方式同时统计多个列?
select t.a, sum(t.b), count(t.c), count(t.d) from some_table t group by t.a;
下面是解决方法:
select t.a, sum(t.b), count(t.c), count(t.d) from (
select a,b,null c,null d from some_table
union al1
seiect a,0 b,c,nul1 d from some_table group by a,c
union al1
seiect a,0 b,nul1 c,d from some_table group by a,d
)t;
1.14.Order By优化
order by只能是在一个reduce进程中进行,所以如果对一个大数据集进行order by ,会导致一个 reduce进程中处理的数据相当大,造成查询执行缓慢。
1.在最终结果上进行order by ,不要在中间的大数据集上进行排序。如果最终结果较少,可以在一个reduce上进行排序时,那么就在最后的结果集上进行order by。
2.如果是取排序后的前N条数据,可以使用distribute by和sort by在各个reduce上进行排序后前N 条,然后再对各个reduce的结果集合合并后在一个reduce中全局排序,再取前N条,因为参与全局排序的 orderby的数据量最多是reduce个数* N,所以执行效率会有很大提升.
在Hive中,关于数据排序,提供了四种语法,一定要区分这四种排序的使用方式和适用场景。
1.order by:全局排序,缺陷是只能使用一个reduce
2.sort by:单机排序,单个reduce结果有序
3.cluster by:对同一字段分桶并排序,不能和sort by连用
4.distribute by + sort by:分桶,保证同一字段值只存在一个结果文件当中,结合sort by保证每 个reduceTask结果有序
Hive HQL中的order by与其他SQL方言中的功能一样,就是将结果按某字段全局排序,这会导致所 有map端解都进入一个reducer中,在数据量大时可能会长时间计算不完。
如果使用sort by,那么会视情况启动多个reducer进行排序,并且保证每个reducer内局部有序。为了控制map端数据分配到reducer的key,往往还要配合distribute by —同使用。如果不加 distribute by的话,map端数据就会随机分配到reducer。
实现全局排序:两种万式:
1,建表导入数据准备
create table if not exists student(id int, name string, sex string, age int, department string) row format delimited fields terminated by ",";
load data local i npath "/home/bigdata/students.txt" into table student;
2.第一种方式
–直接使用order by来做。如果结果数据量很大,这个任务的执行效率会非常低 select id,name,age from student order by age desc limit 3;
3.第二种方式
–使用distribute by + sort by 多个reduceTask,每个reduceTask分别有序 set mapreduce.job.reduces=3; drop table student_orderby_result;
–范围分桶0<18<1<20<2 create table student_orderby_result as seiect * from student distribute by (case when age > 20 then 0 when age < 18 then 2
else 1 end) sort by (age desc);
根据数据规律来确定采样方式
1.15.Count Distinct优化
当要统计某一列去重数时,如果数据量很大,count(distinct)就会非常慢,原因与order by相似, count(distinct)逻辑只会有很少的reducer来处理。这时可以用group by来改写:
--先 group by 在 count
seiect count(l) from (
seiect age from student
where department >= "ma"
group by age
)t;
再来一个例子:
优化前,一个普通的只使用一个reduceTask来进行count(distinct)操作
–优化前(只有一个reduce,先去重再count负担比较大):
select count(distinet id) from tablename;
优化后,但是这样写会启动两个MRjob (单纯distinct只会启动一个),所以要确保数据量大到启动 job的overhead远小于计算耗时,才考虑这种方法。当数据集很小或者key的倾斜比较明显时, group by还可能会比distinct慢。
–优化后(启动两个job, 一个job负责子查询(可以有多个reduce),另一个job负责count(l)):
select count(l) from (select distinct id from tablename) tmp;
select count(l) from (select id from tablename group by i d) tmp; // 推荐使用这种
select t.a, count(t.b), sum(t.c) from t group by t.a;
select t.a, count(distinct t.b, t.c) from t group by t.a;
1.16.怎样 写in/exists 语句
在Hive的早期版本中,in/exists语法是不被支持的,但是从hive-0.8x以后就开始支持这个语法。但是不推荐使用这个语法。虽然经过测验,Hive-2.3.6也支持in/exists操作,
但还是推荐使用Hive的一个 高效替代方案:left semi join
比如说:
--in / exists 实现
seiect a.id, a.name from a where a.id in (seiect b.1d from b);
seiect a.id, a.name from a where exists (seiect id from b where a.id = b.id);
可以使用join来改写:
select a. id, a. namr from a join b on a. id = b. id;
现在可以转换成:
--left semi join 实现
select a. id, a. name from a 1 eft semi join b on a. id = b. id;
1.17.使用 vectorization 技术
在计算类似scan, filter, aggregation的时候,vectorization技术以设置批处理的增量大小为1024行 单次来达到比单条记录单次获得更高的效率。
set hive.vectorized.execution.enabled=true ;
set hive.vectorized.execution.reduce.enabled=true;
1.18.多重模式
如果你碰到一堆SQL,并且这一堆SQL的模式还一样。都是从同一个表进行扫描,做不同的逻辑。 有可优化的地方:如果有n条SQL,每个SQL执行都会扫描一次这张表。
如果一个HQL底层执行10个Job,那么能优化成8个一般来说,肯定能有所提高,多重插入就是一 个非常实用的技能。一次读取,多次插入,有些场景是从一张表读取数据后,要多次利用,这时可以使 用
multi insert
语法:
from sale_detai1
insert overwrite table sale_detail_multi partition (sale_date='2019*, region='china')
select shop_name, customer_id, total_price where
1nsert overwrite table sale_detail_multi partition (sale_date='2020*, region='china')
select shop_name, customer_id, total_price where ;
说明:multi insert语法有一些限制。
1.一般情况下,单个SQL中最多可以写128路输出,超过128路,则报语法错误。
2.在一个multi i nsert中: 对于分区表,同一个目标分区不允许出现多次。 对于未分区表,该表不能出现多次。
3.对于同一张分区表的不同分区,不能同时有insert overwrite和insert into操作,否则报错返回。
Multi-Group by是Hive的一个非常好的特性,它使得Hive中利用中间结果变得非常方便。
例如:
from (select a.status, b.school, b.gender from status_updates a join profi1es b ON (a.userid = b.userid and a.ds='2019-03-20' )) subql
INSERT OVERWRITE table gender_summary PARTiTiON(ds=* 2019-03-20*)
SELECT subql.gender, COUNT(l) GROUP BY subql.gender
INSERT OVERWRITE table school.summary PARTITION(ds=* 2019-03-20*) SELECT subql.school, COUNT(l) GROUP BY subql.school;
上述查询语句使用了 Multi-Group by特性连续group by T 2次数据,使用不同的Multi-Group byo 这一特性可以减少一次MapReduce操作。
1.19.启动中间结果压缩
map输出压缩
set mapreduce.map.output.compress=true;
set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.snappycodec;
中间数据压缩
中间数据压缩就是对hive查询的多个Job之间的数据进行压缩。最好是选择一个节省CPU耗时的压缩方式。可以采用
snappy
压缩算法,该算法的压缩和解压效率都非常高。
set hive.exec.compress.intermediate=true;
set hive.i ntermediate.compression.codec=org.apache.hadoop.io.compress.Snappycodec; set hive.intermediate.compression.type=BLOCK
结果数据压缩
最终的结果数据(Reducer输出数据)也是可以进行压缩的,可以选择一个压缩效果比较好的,可以减少数据的大小和数据的磁盘读写时间;注:常用的gzip, snappy压缩算法是不支持并行处理的,如果数据源是gzip/snappy压缩文件大文件,这样只会有有个mapper来处理这个文件,会严重影响查询效率。所以如果结果数据需要作为其他查询任务的数据源,可以选择支持splitable的lzo算法,这样既能对结果文件进行压缩,还可以并行的处理,这样就可以大大的提高job执行的速度了。
set hive.exec.compress.output=true;
set mapreduce.output.fileoutputformat.compress=true;
set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.GzipCodec;
set mapreduce.output.fileoutputformat.compress.type=BLOCK;
Hadoop集群支持的压缩算法:
org.apache.hadoop.io.compress.Defaultcodec
org.apache.hadoop.io.compress.Gzi pcodec
org.apache.hadoop.io.compress.BZi p2Codec
org.apache.hadoop.io.compress.DeflateCodec
org.apache.hadoop.io.compress.Snappycodec
org.apache.hadoop.io.compress.Lz4codec
com.hadoop.compression.Izo.Lzocodec
com.hadoop.compression.Izo.Lzopcodec
老姜总结
hive语法和参数层面调优是目前我们参考比较居多的,很多场景都会在不同的业务场景使用,调优最好方案是尝试,看效果,适合当下的就是你最好的调优结果。
关注老姜,哔哩号与公众号同步:老姜的数据江湖