Hive第三天(语法调优)

一、查看执行计划

Hive 的 SQL 语句在执行之前需要将 SQL 语句转换成 MapReduce 任务,因此需要了解具体的转换过程,可以在 SQL 语句中输入如下命令查看具体的执行计划。

## 查看执行计划,添加extended关键字可以查看更加详细的执行计划
explain [extended] query

例如:

explain select department, count(*) as total from student where age >= 18 group by department order by total desc limit 3;

Hive 的执行计划中的 Operator 的概念:(逻辑执行计划:Operator Tree)
    查询:select ... from ... where ... group by ... having ... order by .... limit ....
                select: FetchOperator
                from: TableScanOperator
                where+having: FilterOperator

二、列裁剪

列裁剪就是在查询时只读取需要的列,分区裁剪就是只读取需要的分区。当列很多或者数据量很大时,如果 select * 或者不指定分区,全列扫描和全表扫描效率都很低。
Hive 在读数据的时候,可以只读取查询中所需要用到的列,而忽略其他的列。这样做可以节省读取开销:中间表存储开销和数据整合开销。

set hive.optimize.cp = true; ## 列裁剪,取数只取查询中需要用到的列,默认是true
 

例如:表:id,name,sex,age,department
SQL业务需求: select age,count(*) as total from student group by age;

 三、谓词下推

 将 SQL 语句中的 where 谓词逻辑都尽可能提前执行,减少下游处理的数据量。对应逻辑优化器是 PredicatePushDown。

set hive.optimize.ppd=true; ## 默认是true
 

例如:

优化前,a、b表数据查询出来后进行过滤,效率较低。

select a.*, b.* from a join b on a.id = b.id where b.age > 20;

优化后:a表和(过滤后的b表)c表关联

select a.*, c.* from a join (select * from b where age > 20) c on a.id = c.id;

 四、分区裁剪

 列裁剪就是在查询时只读取需要的列,分区裁剪就是只读取需要的分区。当列很多或者数据量很大时,如果 select * 或者不指定分区,全列扫描和全表扫描效率都很低。
在查询的过程中只选择需要的分区,可以减少读入的分区数目,减少读入的数据量。
 

Hive 中与分区裁剪优化相关代码为:

set hive.optimize.pruner=true; ## 默认是true

在 HiveQL 解析阶段对应的则是 ColumnPruner 逻辑优化器。
分区裁剪要求必须是有分区的应用场景。

五、合并小文件

大数据技术组件最怕的就是两个事儿:
1、存储组件:海量小文件
SQL需求;1000000M = 100000 * 10M 10万个小文件
2、计算组件:数据倾斜
SQL需求;1000000M = 100000 * 10M 10万个小文件


如果一个mapreduce job碰到一堆小文件作为输入,一个小文件启动一个Task
在MR编程模型中,给我们提供了一个中叫做 CombineFileInputFormat的一个类:具备 把一个节点甚至一个机架上的多个小文件,划分到同一个 输入切片
Hive的默认INputFormat: TextInputFormat

Map 输入合并
在执行 MapReduce 程序的时候,一般情况是一个文件的一个数据分块需要一个 mapTask 来处理。但是如果数据源是大量的小文件,这样就会启动大量的 mapTask 任务,这样会浪费大量资源。可以将输入的小文件进行合并,从而减少 mapTask 任务数量。


## Map端输入、合并文件之后按照block的大小分割(默认)
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
## Map端输入,不合并
set hive.input.format=org.apache.hadoop.hive.ql.io.HiveInputFormat;

Map/Reduce输出合并
大量的小文件会给 HDFS 带来压力,影响处理效率。可以通过合并 Map 和 Reduce 的结果文件来消除影响。

## 是否合并Map输出文件, 默认值为true
set hive.merge.mapfiles=true;
## 是否合并Reduce端输出文件,默认值为false
set hive.merge.mapredfiles=true;
## 合并文件的大小,默认值为256000000 = 256M
set hive.merge.size.per.task=256000000;
## 每个Map 最大分割大小
set mapred.max.split.size=256000000;
## 一个节点上split的最少值
set mapred.min.split.size.per.node=1; // 服务器节点
# 如果有多个节点,都是只有一个小文件,这种情况当中,意味着压根没合并
# 如果这个节点有300个1M的文件。指定的输入切片大小是:256M, 44M的数据会传送给其他节点做合并
# 如果这个节点有500个1M的文件。指定的输入切片大小是:256M, 244M的数据会传送给其他节点做合并
## 一个机架上split的最少值,需要hadoop支撑
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。

六、合理设置Task并行度

6.1设置MapTask的并行度

        MapTask的数据来源:原始数据, 上一个MR的输出结果

        Map数过大:当输入文件特别大,MapTask 特别多,每个计算节点分配执行的 MapTask 都很多,这时候可以考虑减少 MapTask 的数量。增大每个MapTask 处理的数据量。而且 MapTask 过多,最终生成的结果文件数也太多。

        Map数太小:当输入文件都很大,任务逻辑复杂,MapTask 执行非常慢的时候,可以考虑增加 MapTask 数,来使得每个 MapTask 处理的数据量减少,从而提高任务的执行效率。

        在 MapReduce 的编程案例中,我们得知,一个MapReduce Job 的 MapTask 数量是由输入分片 InputSplit 决定的。而输入分片是由
FileInputFormat.getSplit() 决定的。一个输入分片对应一个 MapTask,而输入分片是由三个参数决定的:

参数默认值意义
dfs.blocksize128MHDFS默认数据块大小
mapreduce.input.fileinputformat.split.minsize1最小分片大小(MR)
mapreduce.input.fileinputformat.split.maxsize256M最大分片大小(MR)

        输入分片大小的计算是这么计算出来的:针对数据是原始数据的情况,最终调整的 splitsize 的大小最好是 blockSize 的整数倍

long splitSize = Math.max(minSize, Math.min(maxSize, blockSize))

默认情况下,输入分片大小和 HDFS 集群默认数据块大小一致,也就是默认一个数据块,启用一个 MapTask 进行处理,这样做的好处是避免了服务器节
点之间的数据传输,提高 job 处理效率
两种经典的控制MapTask的个数方案:减少MapTask数 或者 增加MapTask数

1、减少 MapTask 数是通过合并小文件来实现,这一点主要是针对数据源
2、增加 MapTask 数可以通过控制上一个 job 的 reduceTask 个数
重点注意:不推荐把这个值进行随意设置!
推荐的方式:使用默认的切块大小即可。如果非要调整,最好是切块的N倍数

控制MapTask的方法

1、如果想增加 MapTask 个数,可以设置 mapred.map.tasks 为一个较大的值
2、如果想减少 MapTask 个数,可以设置 maperd.min.split.size 为一个较大的值
3、如果输入是大量小文件,想减少 mapper 个数,可以通过设置 hive.input.format 合并小文件


如果想要调整 mapper 个数,在调整之前,需要确定处理的文件大概大小以及文件的存在形式(是大量小文件,还是单个大文件),然后再设置合适的参数。不能盲目进行暴力设置,不然适得其反。
MapTask 数量与输入文件的 split 数息息相关,在 Hadoop 源码 org.apache.hadoop.mapreduce.lib.input.FileInputFormat 类中可以看到 split
划分的具体逻辑。可以直接通过参数 mapred.map.tasks (默认值2)来设定 MapTask 数的期望值,但它不一定会生效。

6.2 设置ReduceTask的并行度

1、推算机制
hive.exec.reducers.bytes.per.reducer 每个reduceTask处理的最大数据大小
hive.exec.reducers.max reduceTask的上限
2、可以自己根据需求来进行常量设置
如果设置了 mapreduce.job.reduces 为 n 在没有 ordre by 的场景中,就使用 n 个reduceTask

如果 ReduceTask 数量过多,一个 ReduceTask 会产生一个结果文件,这样就会生成很多小文件,那么如果这些结果文件会作为下一个 Job 的输入,会出现小文件需要进行合并的问题,而且启动和初始化 ReduceTask 需要耗费资源。
如果 ReduceTask 数量过少,这样一个 ReduceTask 就需要处理大量的数据,并且还有可能会出现数据倾斜的问题,使得整个查询耗时长。 默认情况
下,Hive 分配的 reducer 个数由下列参数决定:
Hadoop MapReduce 程序中,ReducerTask 个数的设定极大影响执行效率,ReducerTask 数量与输出文件的数量相关。如果 ReducerTask 数太多,会产生大量小文件,对HDFS造成压力。如果 ReducerTask 数太少,每个ReducerTask 要处理很多数据,容易拖慢运行时间或者造成 OOM。这使得 Hive怎样决定 ReducerTask 个数成为一个关键问题。遗憾的是 Hive 的估计机制很弱,不指定 ReducerTask 个数的情况下,Hive 会猜测确定一个
ReducerTask 个数,基于以下两个设定:

参数1:hive.exec.reducers.bytes.per.reducer(默认256M)
参数2:hive.exec.reducers.max(默认为1009)
参数3:mapreduce.job.reduces(默认值为-1,表示没有设置,那么就按照以上两个参数进行设置)

七、Join优化

Join优化的原则:

1、优先过滤后再进行Join操作,最大限度的减少参与join的数据量(where能用就用)
2、小表join大表,最好启动mapjoin,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.id
4、如果多张表做join, 如果多个链接条件都相同,会转换成一个Job


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值