Hive SQL优化常见问题汇总

一、hive动态配置项

hive.optimize.cp=true:列裁剪
hive.optimize.prunner:分区裁剪
hive.limit.optimize.enable=true:优化LIMIT n语句
hive.limit.row.max.size=1000000:
hive.limit.optimize.limit.file=10:最大文件数

1、本地模式(小任务):

需要满足以下条件:

  1. job的输入数据大小必须小于参数:hive.exec.mode.local.auto.inputbytes.max(默认128MB)
  2. job的map数必须小于参数:hive.exec.mode.local.auto.tasks.max(默认4)
  3. job的reduce数必须为0或者1
hive.exec.mode.local.auto.inputbytes.max=134217728
hive.exec.mode.local.auto.tasks.max=4
hive.exec.mode.local.auto=true
hive.mapred.local.mem:本地模式启动的JVM内存大小 

2、 并发执行

hive.exec.parallel=true ,默认为false
hive.exec.parallel.thread.number=8 

3、Strict Mode:

**hive.mapred.mode=true,**严格模式不允许执行以下查询:

  1. 分区表上没有指定分区
  2. 没有limit限制的order by语句
  3. 笛卡尔积:JOIN时没有ON语句

4、动态分区:

hive.exec.dynamic.partition.mode=strict:该模式下必须指定一个静态分区
hive.exec.max.dynamic.partitions=1000
hive.exec.max.dynamic.partitions.pernode=100:在每一个mapper/reducer节点允许创建的最大分区数
DATANODE:dfs.datanode.max.xceivers=8192:允许DATANODE打开多少个文件

--动态分区属性:设置为true表示开启动态分区功能(默认为false)
hive.exec.dynamic.partition=true;

--动态分区属性:设置为nonstrict,表示允许所有分区都是动态的(默认为strict)
--设置为strict,表示必须保证至少有一个分区是静态的
hive.exec.dynamic.partition.mode=nonstrict;
 
--动态分区属性:每个mapper或reducer可以创建的最大动态分区个数
hive.exec.max.dynamic.partitions.pernode=100;

--动态分区属性:一个动态分区创建语句可以创建的最大动态分区个数
hive.exec.max.dynamic.partitions=1000;

--动态分区属性:全局可以创建的最大文件个数
hive.exec.max.created.files=100000;

--控制DataNode一次可以打开的文件个数
--这个参数必须设置在DataNode的$HADOOP_HOME/conf/hdfs-site.xml文件中
<property>
    <name>dfs.datanode.max.xcievers</name>
    <value>8192</value>
</property>  

5、推测执行:

-目的:是通过加快获取单个task的结果以及进行侦测将执行慢的TaskTracker加入到黑名单的方式来提高整体的任务执行效率
mapred.map.tasks.speculative.execution=true
mapred.reduce.tasks.speculative.execution=true
hive.mapred.reduce.tasks.speculative.execution=true;

6、Single MapReduce MultiGROUP BY

hive.multigroupby.singlemar=true:当多个GROUP BY语句有相同的分组列,则会优化为一个MR任务

7、hive.exec.rowoffset:是否提供虚拟列

--当hive产生了非预期的或null的时候,可以通过虚拟列进行诊断,判断哪行数据出现问题

INPUT__FILE__NAME  (输入文件名)
BLOCK__OFFSET__INSIDE__FILE  (块内偏移量)
ROW__OFFSET__INSIDE__BLOCK  (行偏移量,需要设置hive.exec.rowoffset=true;启用) 

示例:
SELECT 
    INPUT__FILE__NAME,
    BLOCK__OFFSET__INSIDE__FILE, 
    ROW__OFFSET__INSIDE__BLOCK,
     substr(regioncode,0,20) 
FROM clickcube_mid 
WHERE length(regioncode) > 100; 

8、善用mapjoin

两个聚集函数不能有不同的DISTINCT列,以下表达式是错误的:

INSERT OVERWRITE TABLE pv_gender_agg SELECT pv_users.gender, count(DISTINCT pv_users.userid), count(DISTINCT pv_users.ip) FROM pv_users GROUP BY pv_users.gender;

SELECT语句中只能有GROUP BY的列或者聚集函数。

9、部分聚集

hive.map.aggr=true;在map中会做部分聚集操作,效率更高但需要更多的内存。
hive.groupby.mapaggr.checkinterval:在Map端进行聚合操作的条目数目

10、数据倾斜时负载均衡

**hive.groupby.skewindata=true:**数据倾斜时负载均衡,当选项设定为true,生成的查询计划会有两个MRJob。第一个MRJob 中,
Map的输出结果集合会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的GroupBy Key 有可能被分发到不同的Reduce中,从而达到负载均衡的目的;第二个MRJob再根据预处理的数据结果按照GroupBy Key分布到 Reduce中(这个过程可以保证相同的GroupBy Key被分布到同一个Reduce中),最后完成最终的聚合操作。

11、谓词下推

关系型数据库借鉴而来,关系型数据中谓词下推到外部数据库用以减少数据传输
基本思想:尽可能早的处理表达式
属于逻辑优化,优化器将谓词过滤下推到数据源,使物理执行跳过无关数据
参数打开设置:hive.optimize.ppd=true

12、CBO优化 Cost based Optimizer

  1. Hive使用Calcite CBO优化流程及SQL优化实战
  2. Hive 通过关闭CBO (Cost based Optimizer) 来优化特定的SQL执行
set hive.cbo.enable=true;
set hive.compute.query.using.stats=true;
set hive.stats.fetch.column.stats=true;
set hive.stats.fetch.partition.stats=true;

13、合并小文件

hive.merg.mapfiles=true:合并map输出
hive.merge.mapredfiles=false:合并reduce输出
hive.merge.size.per.task=25610001000:合并文件的大小
hive.mergejob.maponly=true:如果支持CombineHiveInputFormat则生成只有Map的任务执行merge
hive.merge.smallfiles.avgsize=16000000:文件的平均大小小于该值时,会启动一个MR任务执行merge

文件数目过多,会给 HDFS 带来压力,并且会影响处理效率,可以通过合并 Map 和 Reduce 的结果文件来消除这样的影响:

· hive.merge.mapfiles = true是否和并 Map 输出文件,默认为 True
· hive.merge.mapredfiles = false是否合并 Reduce 输出文件,默认为 False
· hive.merge.size.per.task = 25610001000合并文件的大小

14、map/reduce数目

减少map数目:

set mapred.max.split.size
set mapred.min.split.size
set mapred.min.split.size.per.node
set mapred.min.split.size.per.rack
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat

增加map数目:
当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。
假设有这样一个任务:

select data_desc, count(1), count(distinct id),sum(case when),sum(case when ...),sum() from a group by data_desc

如果表a只有一个文件,大小为120M,但包含几千万的记录,如果用1个map去完成这个任务,肯定是比较耗时的,这种情况下,我们要考虑将这一个文件合理的拆分成多个,这样就可以用多个map任务去完成。

set mapred.reduce.tasks=10;
create table a_1 as select * from a distribute by rand(123);

这样会将a表的记录,随机的分散到包含10个文件的a_1表中,再用a_1代替上面sql中的a表,则会用10个map任务去完成。每个map任务处理大于12M(几百万记录)的数据,效率肯定会好很多。

reduce数目设置:
 参数1:hive.exec.reducers.bytes.per.reducer=1G:每个reduce任务处理的数据量
 参数2:hive.exec.reducers.max=999(0.95TaskTracker数):每个任务最大的reduce数目
 reducer数=min(参数2,总输入数据量/参数1)
 set mapred.reduce.tasks:每个任务默认的reduce数目。典型为0.99
reduce槽数,hive将其设置为-1,自动确定reduce数目。

  1. 尽量避免使用order by ,order by时只有一个reducer

15、使用索引

hive.optimize.index.filter:自动使用索引
hive.optimize.index.groupby:使用聚合索引优化GROUP BY操作

16、多重模式

17、Fetch抓取

Fetch 抓取是指,Hive 中对某些情况的查询可以不必使用 MapReduce 计算。例如:SELECT

  • FROM emp;在这种情况下,Hive 可以简单地读取 emp 对应的存储目录下的文件,然后输出
    查询结果到控制台。
    在 hive-default.xml.template 文件中** hive.fetch.task.conversion **默认是 more,老版本 hive
    默认是 minimal,该属性修改为 **more **以后,在全局查找、字段查找、limit 查找等都不走

18、jvm重用

二、hive查询优化

1、from->join on

1. 笛卡尔积和小表Join大表 优化

  1. 设置严格模式
  2. 小表在前大表在后
  3. 开启map join代替common join

优化使用:配置如下参数,使用mapjoin替代common join.当然这里因为group by的原因还是会启动reduce进行去重。一般来说小表join大表一般配置下面四个参数就差不多,当然官方还提供了其他的参数共配置Hive官网参数配置

set hive.auto.convert.join = true ;   -- hive是否自动根据文件量大小,选择将common join转成map join 。hive 0.10 版本后的默认值 true。
set  hive.mapjoin.smalltable.filesize =25000000 ;大表小表判断的阈值,如果表的大小小于该值25Mb,则会被判定为小表。则会被加载到内存中运行,将commonjoin转化成mapjoin。一般这个值也就最多几百兆的样子。
set  hive.auto.convert.join.noconditionaltask = true;  是否启用基于输入文件的大小,将普通连接转化为Map连接的优化机制。假设参与连接的表(或分区)有N个,如果打开这个参数,并且有N-1个表(或分区)的大小总和小于hive.auto.convert.join.noconditionaltask.size参数指定的值,那么会直接将连接转为Map连接。(说人话:默认值:true,当将普通的join转化为普通的mapjoin时,是否将多个mapjoin转化为一个mapjoin,主要针对多个小表join大表的情形)

![image.png](https://img-blog.csdnimg.cn/img_convert/837990689b2e3b1fdd7e94b3b241d342.png#clientId=u5bf32e96-8619-4&from=paste&id=u91969e9c&margin=[object Object]&name=image.png&originHeight=474&originWidth=1067&originalType=url&ratio=1&size=125504&status=done&style=none&taskId=u823644e2-7919-4b84-b0bf-7062f940611)
**Join查找操作的基本原则:**应该将条目少的表/子查询放在 Join 操作符的左边。原因是在 Join 操作的 Reduce 阶段,位于 Join 操作符左边的表的内容会被加载进内存,将条目少的表放在左边,可以有效减少发生内存溢出错误的几率。

Join查找操作中如果存在多个join,且所有参与join的表中其参与join的key都相同,则会将所有的join合并到一个mapred程序中。

案例:

SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)  在一个mapre程序中执行join
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2)   在两个mapred程序中执行join

Map join的关键在于join操作中的某个表的数据量很小,案例:

SELECT /+ MAPJOIN(b) / a.key, a.value  FROM a join b on a.key = b.key

Mapjoin 的限制是无法执行a FULL/RIGHT OUTER JOIN b,和map join相关的hive参数:

hive.join.emit.interval  
hive.mapjoin.size.key
hive.mapjoin.cache.numrows

由于join操作是在where操作之前执行,所以当你在执行join时,where条件并不能起到减少join数据的作用;案例:

SELECT a.val, b.val FROM a LEFT OUTER JOIN b ON (a.key=b.key)
WHERE a.ds='2009-07-07' AND b.ds='2009-07-07'

最好修改为:

SELECT a.val, b.val FROM a LEFT OUTER JOIN b
ON (a.key=b.key AND b.ds='2009-07-07' AND a.ds='2009-07-07')

在join操作的每一个mapred程序中,hive都会把出现在join语句中相对靠后的表的数据stream化,相对靠前的变的数据缓存在内存中。当然,也可以手动指定stream化的表:SELECT /_+ STREAMTABLE(a) _/ a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)

2. Join on:大表、大表 SMB Join(重点)

2、where

1. 指定分区

2. 尽量避免在where子句中使用函数操作

3. 应尽量避免在where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描

3、select

1. 避免使用需要全表扫描和全列扫描

  1. Hive实现(not) in

通过left outer join进行查询,(假设B表中包含另外的一个字段 key1

select a.key from a left outer join b on a.key=b.key where b.key1 is null

通过left semi join 实现 in

SELECT a.key, a.val FROM a LEFT SEMI JOIN b on (a.key = b.key)

Left semi join 的限制:join条件中右边的表只能出现在join条件中。

  1. 少用order by ,或者加上limit,开启严格模式
  2. 慎用in,not in,exist,like
  3. 避免模糊查询
  4. 少用is null
  5. 少用<>,i=这种不等于操作符
  6. or 语句使用不当
  7. 应尽量避免在where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描
  8. 尽量避免在where子句中使用函数操作

2. Distinct 使用

Hive支持在group by时对同一列进行多次distinct操作,却不支持在同一个语句中对多个列进行distinct操作。

3. select: UDTF

UDTF将单一输入行转化为多个输出行,并且在使用UDTF时,select语句中不能包含其他的列,UDTF不支持嵌套,也不支持group by 、sort by等语句。如果想避免上述限制,需要使用lateral view语法,案例:

select a.timestamp, get_json_object(a.appevents, '.eventname') from log a;
select a.timestamp, b.*
from log a lateral view json_tuple(a.appevent, 'eventid', 'eventname') b as f1, f2;

其中,get_json_object为UDF函数,json_tuple为UDTF函数。
UDTF函数在某些应用场景下可以大大提高hql语句的性能,如需要多次解析json或者xml数据的应用场景。

4. select:聚合函数count和sum

Count和sum函数可能是在hql语句中使用的最为频繁的两个聚合函数了,但是在hive中count函数在计算distinct value时支持加入条件过滤。

5. select:善用count(distinct)

4、group by

1. group by 优化

Map端聚合,首先在map端进行初步聚合,最后在reduce端得出最终结果,相关参数:

· hive.map.aggr = true是否在 Map 端进行聚合,默认为 True
· hive.groupby.mapaggr.checkinterval = 100000在 Map 端进行聚合操作的条目数目

数据倾斜聚合优化,设置参数hive.groupby.skewindata = true,当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。

5、having

6、order

1. 排序优化

  1. Order by 实现全局排序,一个reduce实现,效率低
  2. Sort by 实现部分有序,单个reduce输出的结果是有序的,效率高,通常和DISTRIBUTE BY关键字一起使用(DISTRIBUTE BY关键字 可以指定map 到 reduce端的分发key)
  3. DISTRIBUTE BY
  4. CLUSTER BY col1 等价于DISTRIBUTE BY col1 SORT BY col1

7、multi insert,union all

1. multi insert

  1. 适合基于同一个源表按照不同逻辑不同粒度处理插入不同表的场景,做到只需要扫描源表一次,job个数不变,减少源表扫描次数

2. union all

  1. 用好,可减少表的扫描次数,减少job的个数,通常预先按不同逻辑不同条件生成的查询union all后,再统一group by计算,不同表的union all相当于multiple inputs,同一个表的union all,相当map一次输出多条

8、Hql使用自定义的mapred脚本

**注意事项:**在使用自定义的mapred脚本时,关键字MAP REDUCE 是语句SELECT TRANSFORM ( … )的语法转换,并不意味着使用MAP关键字时会强制产生一个新的map过程,使用REDUCE关键字时会产生一个reduce过程。

自定义的mapred脚本可以是hql语句完成更为复杂的功能,但是性能比hql语句差了一些,应该尽量避免使用,如有可能,使用UDTF函数来替换自定义的mapred脚本

9、Vectorization矢量计算

10、数据倾斜优化

  1. join 时 数据倾斜优化
    1. 大表join大表优化
    2. 小表join大表优化
  2. group by 时数据倾斜优化
  3. 窗口函数 partition by时数据倾斜优化
  4. count(distinct)时数据倾斜优化

你可能感兴趣的:(hive)