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需要耗费资源。