hive性能调优详解系列之hive语法和参数层面调优_数仓工程师必备hive调优方案

  • Post author:
  • Post category:其他


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语法和参数层面调优是目前我们参考比较居多的,很多场景都会在不同的业务场景使用,调优最好方案是尝试,看效果,适合当下的就是你最好的调优结果。


关注老姜,哔哩号与公众号同步:老姜的数据江湖



版权声明:本文为wei_bigdata原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。