Hive企业级调优--万字详解 图文并茂【史上最全】

目录

Yarn和MR资源配置

Yarn资源配置

MR资源配置

Explain查看执行计划

分组聚合优化

优化前VS优化后

Join优化

Common Join(普通join)

原理

Map Join

原理

优化 

法一:hint提示

法二:自动触发

 优化案例

Bucket Map Join

原理

优化

hint提示 

 优化案例

Sort Merge Bucket Map Join(SMB map join)

原理

优化

数据倾斜优化

分组聚合导致的数据倾斜

Map-Side聚合

Skew-GroupBy优化

优化前

优化后 

Join导致的数据倾斜

map join

skew join

任务并行度优化

Map端并行度

Reduce端并行度

小文件合并优化

合并Map端输入的小文件

合并Reduce端输出的小文件

其他优化

CBO优化

谓词下推

矢量化查询

Fetch抓取

本地模式

并行执行

严格模式


Yarn和MR资源配置

配置项参考官网:https://apache.github.io/hadoop/

Yarn资源配置

修改yarn-site.xml,调整的Yarn参数均与CPU、内存等资源有关,配置如下:

<property>

    <name>yarn.nodemanager.resource.memory-mb</name>

    <value>65536</value>

    <description>一个NodeManager节点分配给Container使用的内存。该参数的配置,取决于NodeManager所在节点的总内存容量和该节点运行的其他服务的数量</description>

</property>

<property>

    <name>yarn.nodemanager.resource.cpu-vcores</name>

    <value>16</value>

    <description>一个NodeManager节点分配给Container使用的CPU核数。该参数的配置,同样取决于NodeManager所在节点的总CPU核数和该节点运行的其他服务。</description>

</property>

<property>

    <name>yarn.scheduler.maximum-allocation-mb</name>

    <value>16384</value>

    <description>单个Container能够使用的最大内存。</description>

</property>

<property>

    <name>yarn.scheduler.minimum-allocation-mb</name>

    <value>512</value>

    <description>单个Container能够使用的最小内存。</description>

</property>

修改后重新分发该配置文件并重启Yarn 

MR资源配置

MapReduce资源配置主要包括Map Task的内存和CPU核数,以及Reduce Task的内存和CPU核数。核心配置参数如下:

1mapreduce.map.memory.mb 

该参数的含义是,单个Map Task申请的container容器内存大小,其默认值为1024。该值不能超出yarn.scheduler.maximum-allocation-mb和yarn.scheduler.minimum-allocation-mb规定的范围。

该参数需要根据不同的计算任务单独进行配置,在hive中,可直接使用如下方式为每个SQL语句单独进行配置:set  mapreduce.map.memory.mb=2048;

2mapreduce.map.cpu.vcores    

该参数的含义是,单个Map Task申请的container容器cpu核数,其默认值为1。该值一般无需调整。如需调整要修改mapred-site.xml文件(mapred-default.xml)

3)mapreduce.reduce.cpu.vcores

该参数的含义是,单个Reduce Task申请的container容器cpu核数,其默认值为1。该值一般无需调整。如需调整要修改mapred-site.xml文件(mapred-default.xml)

4)mapreduce.reduce.memory.mb     

该参数的含义是,单个Reduce Task申请的container容器内存大小,其默认值为1024。该值同样不能超出yarn.scheduler.maximum-allocation-mb和yarn.scheduler.minimum-allocation-mb规定的范围。

该参数需要根据不同的计算任务单独进行配置,在hive中,可直接使用如下方式为每个SQL语句单独进行配置:set  mapreduce.reduce.memory.mb=2048;

Explain查看执行计划

Explain用于呈现HQL语句的详细执行步骤,由一系列Stage组成,简单的理解为HQL查询语句的不同执行阶段,这一系列Stage具有依赖关系,每个Stage对应一个MapReduce Job或一个文件系统操作等。

若某个Stage对应的一个MapReduce Job,则其Map端和Reduce端的计算逻辑分别由Map Operator Tree和Reduce Operator Tree进行描述,Operator Tree由一系列的Operator组成,一个Operator代表在Map或Reduce阶段的一个单一的逻辑操作,例如TableScan Operator,Select Operator,Join Operator等。具体如下图:

常见的Operator及其作用如下

TableScan:表扫描操作,通常map端第一个操作肯定是表扫描操作

Select Operator:选取操作

Group By Operator:map端的分组聚合操作,在后面的分组聚合中会讲到

Reduce Output Operator:输出到 reduce 操作

Filter Operator:过滤操作

Join Operator:join 操作

File Output Operator:文件输出操作

Fetch Operator 客户端获取数据操作

Explain语法 

EXPLAIN [FORMATTED | EXTENDED | DEPENDENCY] query-sql

  • FORMATTED:将执行计划以JSON字符串的形式输出
  • EXTENDED:输出执行计划中的额外信息,通常是读写的文件名等信息
  • DEPENDENCY:输出执行计划读取的表及分区

例:

hive (default)>

explain formatted

select user_id,count(*)  from order_detail group by user_id;

分组聚合优化

分组聚合是通过MR Job实现的,map端读取数据,并按照分组字段分区,通过shuffle,把数据发到reduce,各组数据在reduce端完成最终的聚合运算。

分组聚合的优化主要围绕减少shuffle数据量进行,具体做法是map-side聚合。map-side聚合是在map端维护一个hash table,先利用其完成数据的部分聚合,再把聚合的结果按照分组字段分区,发到reduce端完成最终聚合,以此提高分组聚合运算效率。简而言之就是增加了一个map端的部分聚合过程,以减少shuffle的工作量,进而减少reduce端的聚合工作量。

map-side聚合相关参数如下

--启用map-side聚合,默认是true

set hive.map.aggr=true;

--用于检测源表数据是否适合进行map-side聚合。检测的方法是:系统自动先对若干条数据进行map-side聚合,若聚合后的条数和聚合前的条数比值小于该值,则认为该表适合进行map-side聚合;否则,认为该表数据不适合进行map-side聚合,后续数据便不再进行map-side聚合。0.5意味着平均有2条数据可以聚合成1条,1意味着没有出现任何的聚合

set hive.map.aggr.hash.min.reduction=0.5;

--用于hive.map.aggr.hash.min.reduction=0.5检测源表是否适合map-side聚合的条数。

set hive.groupby.mapaggr.checkinterval=100000;

--map-side聚合所用的hash table占用map task堆内存的最大比例,若超出该值,则会对hash table进行一次flush。

set hive.map.aggr.hash.force.flush.memory.threshold=0.7;

优化前VS优化后

set hive.map.aggr=false关闭分组聚合优化,查看执行效果,在Map端没有了Group By Operator

 set hive.map.aggr=true开启分组聚合优化,查看执行效果,在Map端有了Group By Operator,

发生map-side优化,优化后比优化前的HQL执行耗时应该有所减少,且map的output数量明显小于input数量。

 若没有触发map-side,则map的output数量虽然比input数量有所减少但可以忽略不计。具体有没有触发map-side可以去web UI界面查看map日志。

注意!!map-side聚合不够智能,即map端的分组聚合是否执行一定程度上会受到分组字段在表中存储的位置和分布的影响,这是底层存储问题,未必是因为数据真的不适合分组聚合。要解决此问题可以提前对数据分区分桶,使用分区分桶表,使得同一区域存储的数据分布具有一定的相似性,这样聚合结果会有所提升。

例:

1)select province_id,count(*) from order_detail group by province_id;

该语句查询所有订单,根据省份id分组聚合,省份只有34个,这样map后的数据应该只有34条,所以聚合结果是应该是比较可观的。所以group by 的基数越小,一般越适合聚合。 

2)select product_id,count(*) from order_detail group by product_id;

若product_id这一分组字段在order_detail表中分布比较散,那么可能会导致hive在表中切片抽样进行map-side检测的时候测试聚合结果>0.5,那么最终就没有使用map-side聚合。所以说如果能保证抽样数据的测试结果<=0.5,就会实现分组聚合,当然也可以调整hive.map.aggr.hash.min.reduction的值以提高map-side的命中率。

若100w的数据集分组聚合之后的输出>100w,可能的原因是多次触发了hash tableflush

Join优化

Join优化就是控制HQL语句走哪种join算法,这些join算法有的快,有的慢,有的激进,有的保守。我们要做的就是让HQL走最适合自己的join算法。

Common Join(普通join)

原理

hive中最稳定的join算法,其通过一个MapReduce Job完成一个join操作。Map端负责读取join操作所需表的数据,并按照关联字段进行分区,通过Shuffle,将其发送到Reduce端,相同key的数据在Reduce端完成最终的Join操作。

 需要注意的是,HQL语句中的join操作和执行计划中的Common Join任务并非一对一的关系,即HQL中的A表 join B表 join C表在common join中未必也是两个join操作,一个HQL语句中的相邻的且关联字段相同的多个join操作可以合并为一个Common Join任务。

例:

1)hive (default)>

select  a.val,  b.val, c.val from

a join b on (a.key = b.key1) join c on (c.key = b.key1)

上述sql语句中两个join操作的关联字段均为b表的key1字段,则该语句中的两个join操作可由一个Common Join任务实现,也就是可通过1个Map Reduce任务实现。

2)hive (default)> select a.val, b.val, c.val from

a join b on (a.key = b.key1) join c on (c.key = b.key2)

上述sql语句中的两个join操作关联字段各不相同,则该语句的两个join操作需要各自通过一个Common Join任务实现,也就是通过2个Map Reduce任务实现。

Map Join

原理

Map Join算法可以通过一个MR和一个MapJoin阶段完成一个join操作,省去了shuffle和reduce,在第二个map阶段进行表的join,不需要进入reduce阶段。其适用场景为大表join小表。第一个Job会读取小表数据,将其制作为hash table,并上传至Hadoop分布式缓存(本质上是上传至HDFS)。第二个Job会先从分布式缓存中读取小表数据,并缓存在Map Task的内存中,然后扫描大表数据,这样在map端即可完成关联操作。如下图所示:

mapreduce local task是本地任务,读取小表数据,因为小表数据占用内存资源少,所以不上传到yarn,直接在本地读取效率更高 ,读取后序列化生成hash table并上传到hdfs的cache中。

其中Mapper是实现Map阶段功能的代码组件。它接受原始数据作为输入,执行某种转换操作,然后输出一组键值对。这些键值对会作为Reduce阶段的输入。

例:
SELECT a.key, a.value FROM a JOIN b ON a.key = b.key
前提b表是一张小表,具体小表有多小,由参数hive.mapjoin.smalltable.filesize来决定,默认值是25M。

参数列表:

1)小表自动选择Mapjoin
set hive.auto.convert.join=true;
默认值:false。该参数为true时,Hive自动对左边的表统计量,若是小表就加入内存,即对小表使用Map join
2)小表阀值
set hive.mapjoin.smalltable.filesize=25000000;
默认值:25M

优化 

法一:hint提示

手动指定通过map join算法,该方式已经过时,不推荐使用。

hive (default)> select /*+ map join(ta) */

ta.id, tb.id  from  table_a  ta  join table_b tb  on ta.id=tb.id;

法二:自动触发

Hive在编译HQL语句阶段,起初所有的join操作均采用Common Join算法实现

之后在物理优化阶段,Hive会根据每个Common Join任务所需表的大小判断该Common Join任务是否能够转换为Map Join任务,若满足要求(小表大小<指定的阈值),便将Common Join任务自动转换为Map Join任务。 

但有些Common Join任务所需的表大小,在HQL的编译阶段是未知的(例如对子查询进行join操作),所以这种Common Join任务是否能转换成Map Join任务在编译阶是无法确定的。

针对这种情况,Hive会在编译阶段生成一个条件任务(Conditional Task),其下会包含一个计划列表,计划列表中包含转换后的Map Join任务以及原有的Common Join任务。最终具体采用哪个计划,是在运行时决定的。大致思路如下图所示:

Map join自动转换的具体判断逻辑如下图所示:

图片详情看尚硅谷P135 

寻找大表候选人时还不知道每张表的大小,那么选择规则是看join方式,有innner join、left join、right join等等。

inner join:每个表都可能是大表候选人。

left join:默认左表为大表候选人,右表当作小表,这样小表会缓存到内存中,以大表为主,从大表中一条条join内存中的小表,如果反过来把大表缓存到内存中,以小表为主,从小表中一条条join内存中的大表,若出现大表有该字段而小表没有的情况,这种情况下就会出现大量数据join失败,小表数据少,大表数据多,那么会因为小表浪费很多数据,所以通常是左表为大表,右表为小表。

right join:左表当作小表,右表为大表候选人。

full outer join:找不到大表候选人,因为全外联要返回两个表的全部数据,两个表都要去遍历,就无法map join优化。

涉及参数:

--启动Map Join自动转换

set hive.auto.convert.join=true;

--一个Common Join operator转为Map Join operator的判断条件:若该Common Join相关的表中,把每一个表都当作大表候选人,若除大表之外的任意一张已知大小的表的大小>大表候选人,则该组合不成立,不生成map join,反之生成一个Map Join计划。此时可能存在多种组合均满足该条件,hive会为每种满足条件的组合均生成一个Map Join计划,同时还会保留原有的Common Join计划作为后备(back up)计划,实际运行时,优先执行Map Join计划,若不能执行成功,则启动Common Join后备计划。

set hive.mapjoin.smalltable.filesize=250000;

--开启无条件转Map Join

set hive.auto.convert.join.noconditionaltask=true;

--无条件转Map Join时的小表之和阈值,若一个Common Join operator相关的表中,存在n-1张表的大小总和<=该值,此时hive便不会再为每种n-1张表的组合均生成Map Join计划,同时也不会保留Common Join作为后备计划。而是只生成一个最优的Map Join计划。

set hive.auto.convert.join.noconditionaltask.size=10000000; 

 优化案例

hive (default)> select from order_detail od

join product_info product on od.product_id = product.id

join province_info province on od.province_id = province.id;

优化前

上述SQL语句共有三张表进行两次join操作,且两次join操作的关联字段不同。故优化前的执行计划应该包含两个Common Join operator,也就是由两个MapReduce任务实现。执行计划如下图所示:

优化思路

使用如下语句获取表/分区的大小信息:

hive (default)>

desc formatted table_name partition(partition_col='partition');

经分析,参与join的三张表,数据量如下:

表名

大小

order_detail

1176009934(约1122M)

product_info

25285707(约24M)

province_info

369(约0.36K)

方案一:

启用Map Join自动转换。

set hive.auto.convert.join=true;

不使用无条件转Map Join。

set hive.auto.convert.join.noconditionaltask=false;

调整hive.mapjoin.smalltable.filesize参数,使其大于等于product_info。

set hive.mapjoin.smalltable.filesize=25285707;

这样可保证将两个Common Join operator均可转为Map Join operator,并保留Common Join作为后备计划,保证计算任务的稳定。调整完的执行计划如下图:

方案二:

启用Map Join自动转换。

set hive.auto.convert.join=true;

使用无条件转Map Join。

set hive.auto.convert.join.noconditionaltask=true;

调整hive.auto.convert.join.noconditionaltask.size参数,使其大于等于product_info和province_info之和。

set hive.auto.convert.join.noconditionaltask.size=25286076;

这样可直接将两个Common Join operator转为两个Map Join operator,并且由于两个Map Join operator的小表大小之和小于等于hive.auto.convert.join.noconditionaltask.size,故两个Map Join operator任务可合并为同一个。这个方案计算效率最高,但需要的内存也是最多的。

调整完的执行计划如下图:

方案三:

启用Map Join自动转换。

set hive.auto.convert.join=true;

使用无条件转Map Join。

set hive.auto.convert.join.noconditionaltask=true;

调整hive.auto.convert.join.noconditionaltask.size参数,使其等于product_info。

set hive.auto.convert.join.noconditionaltask.size=25285707;

这样可直接将两个Common Join operator转为Map Join operator,但不会将两个Map Join的任务合并。该方案计算效率比方案二低,但需要的内存也更少。

调整完的执行计划如下图:

Bucket Map Join

原理

Bucket Map Join是对Map Join算法的改进,其打破了Map Join只适用于大表join小表的限制,可用于大表join大表的场景分桶其实就是把大表化成了“小表”,然后 Map-Side Join 解决。

Bucket Map Join的核心思想是:若能保证参与join的表均为分桶表,且关联字段为分桶字段,且其中一张表的分桶数量是另外一张表分桶数量的整数倍,就能保证参与join的两张表的分桶之间具有明确的关联关系,所以就可以在两表的分桶间进行Map Join操作了。这样一来,第二个Job的Map端就无需再缓存小表的全表数据了,而只需缓存其所需的分桶即可。其原理如图所示:

第一个map对较小的表tableB的每个bucket序列化成hash table,上传到hdfs cache中,第二个map对较大的表tableA的每个桶单独切片,有几个桶就有几个mapper

优化

hint提示 

Bucket Map Join不支持自动转换,啊!原来是hive团队在hive2.x已经放弃维护MR计算引擎,建议使用spark等计算引擎(看到这乐死我了tmd白学了)。

参数:

--关闭cbo优化,cbo会导致hint信息被忽略

set hive.cbo.enable=false;

--map join hint默认会被忽略(因为已经过时),需将如下参数设置为false

set hive.ignore.mapjoin.hint=false;

--启用bucket map join优化功能

set hive.optimize.bucketmapjoin = true;

 优化案例

hive (default)> select * from( select from order_detail  where dt='2020-06-14') od

join( select * from payment_detail  where dt='2020-06-14') pd on od.id=pd.order_detail_id;

优化前

上述SQL语句共有两张表一次join操作,故优化前的执行计划应包含一个Common Join任务,通过一个MapReduce Job实现。执行计划如下图所示:

优化思路

经分析,参与join的两张表,数据量如下。

表名

大小

order_detail

1176009934(约1122M)

payment_detail

334198480(约319M)

两张表都相对较大,若采用普通的Map Join算法,则Map端需要较多的内存来缓存数据,可以选择为Map段分配更多的内存,来保证任务运行成功。但是,Map端的内存不可能无上限的分配,所以当参与Join的表数据量均过大时,可以考虑采用Bucket Map Join算法。

 创建两个分桶表,order_detail建议分16个bucket,payment_detail建议分8个bucket,注意分桶个数的倍数关系以及分桶字段。然后向其中导入数据。

设置优化参数:

--关闭cbo优化,cbo会导致hint信息被忽略,需将如下参数修改为false

set hive.cbo.enable=false;

--map join hint默认会被忽略(因为已经过时)需将如下参数修改为false

set hive.ignore.mapjoin.hint=false;

--启用bucket map join优化功能,默认不启用,需将如下参数修改为true

set hive.optimize.bucketmapjoin = true;

重写SQL语句:

hive (default)>

select /*+ mapjoin(pd) */ * from order_detail_bucketed od

join payment_detail_bucketed pd on od.id = pd.order_detail_id;

执行结果如下: 

使用

hive (default)>

explain extended select /*+ mapjoin(pd) */ *

from order_detail_bucketed od

join payment_detail_bucketed pd on od.id = pd.order_detail_id;查看执行计划,在Map Join Operator中看到 “BucketMapJoin: true

Sort Merge Bucket Map Join(SMB map join)

原理

SMB Map Join基于Bucket Map Join。SMB Map Join要求,参与join的表均为分桶表,且需保证分桶内的数据是有序的,且分桶字段、排序字段和关联字段为相同字段,且其中一张表的分桶数量是另外一张表分桶数量的整数倍。

SMB Map Join同Bucket Join一样,同样是利用两表各分桶之间的关联关系,在分桶之间进行join操作,不同的是,分桶之间的join操作的实现原理。Bucket Map Join,两个分桶之间的join实现原理为Hash Join算法;而SMB Map Join,两个分桶之间的join实现原理为Sort Merge Join算法。

Hash Join和Sort Merge Join均为关系型数据库中常见的Join实现算法。Hash Join的原理相对简单,就是对参与join的一张表构建hash table,然后扫描另外一张表,然后进行逐行匹配。Sort Merge Join需要在两张按照关联字段排好序的表中进行,其原理如图所示:

Hive中的SMB Map Join就是对两个分桶的数据按照上述思路进行Join操作。可以看出,SMB Map Join与Bucket Map Join相比,在进行Join操作时,Map端是无需对整个Bucket构建hash table,也无需在Map端缓存整个Bucket数据的,每个Mapper只需按顺序逐个key读取两个分桶的数据进行join即可。 

优化

Sort Merge Bucket Map Join有两种触发方式,包括Hint提示和自动转换。Hint提示已过时,不推荐使用。下面是自动转换的相关参数:

 --启动Sort Merge Bucket Map Join优化

set hive.optimize.bucketmapjoin.sortedmerge=true;

--使用自动转换SMB Join

set hive.auto.convert.sortmerge.join=true; 

和bucket map join一样,创建分桶表并导入数据 ,设置参数,运行HQL,结果如下:

数据倾斜优化

数据倾斜问题,通常是指参与计算的数据分布不均,即某个key或者某些key的数据量远超其他key,导致在shuffle阶段,大量相同key的数据被发往同一个Reduce,进而导致该Reduce所需的时间远超其他Reduce,成为整个任务的瓶颈。

Hive中的数据倾斜常出现在分组聚合和join操作的场景中,下面分别介绍在上述两种场景下的优化思路。

分组聚合导致的数据倾斜

Hive中未经优化的分组聚合,是通过一个MapReduce Job实现的。Map端负责读取数据,并按照分组字段分区,通过Shuffle,将数据发往Reduce端,各组数据在Reduce端完成最终的聚合运算。

如果group by分组字段的值分布不均,就可能导致大量相同的key进入同一Reduce,从而导致数据倾斜问题。

由分组聚合导致的数据倾斜问题,有以下两种解决思路:

Map-Side聚合

前文提过,此处略过

Skew-GroupBy优化

原理是启动两个MR任务,第一个MR按照随机数分区,将数据分散发送到Reduce,完成部分聚合,第二个MR把打散的数据按照分组字段分区,完成最终聚合。

优化前

该表数据中的province_id字段是存在倾斜的,若不经过优化,通过观察任务的执行过程,是能够看出数据倾斜现象的。

 

优化后 

--启用skew-groupby

set hive.groupby.skewindata=true;

--关闭map-side聚合(map side聚合默认是开启的)

set hive.map.aggr=false;

开启Skew-GroupBy优化后,可以很明显看到该sql执行在yarn上启动了两个mr任务,第一个mr打散数据,第二个mr把打散后的数据进行分组聚合。 

Join导致的数据倾斜

未经优化的join操作,默认是使用common join算法,也就是通过一个MapReduce Job完成计算。Map端负责读取join操作所需表的数据,并按照关联字段进行分区,通过Shuffle,将其发送到Reduce端,相同key的数据在Reduce端完成最终的Join操作。

如果关联字段的值分布不均,就可能导致大量相同的key进入同一Reduce,从而导致数据倾斜问题。由join导致的数据倾斜问题,有如下三种解决方案:

map join

略过

skew join

原理是为倾斜的大key单独启动一个map join任务进行计算,其余key进行正常的common join。原理图如下:

--启用skew join优化

set hive.optimize.skewjoin=true;

--触发skew join的阈值,若某个key的行数超过该参数值,则触发

set hive.skewjoin.key=100000;

这种方案对参与join的源表大小没有要求,但是对两表中倾斜的key的数据量有要求,要求一张表中的倾斜key的数据量比较小(方便走map join)。

任务并行度优化

Hive的计算任务由MapReduce完成,故并行度的调整需要分为Map端和Reduce端。

Map端并行度

Map端的并行度,也就是Map的个数。是由输入文件的切片数决定的。一般情况下,Map端的并行度无需手动调整。

以下特殊情况可考虑调整map端并行度:

1)查询的表中存在大量小文件

按照Hadoop默认的切片策略,一个小文件会单独启动一个map task负责计算。若查询的表中存在大量小文件,则会启动大量map task,造成计算资源的浪费。这种情况下,可以使用Hive提供的CombineHiveInputFormat,多个小文件合并为一个切片,从而控制map task个数。相关参数如下:

set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

2map端有复杂的查询逻辑

若SQL语句中有正则替换、json解析等复杂耗时的查询逻辑时,map端的计算会相对慢一些。若想加快计算速度,在计算资源充足的情况下,可考虑增大map端的并行度,令map task多一些,每个map task计算的数据少一些。相关参数如下:

--一个切片的最大值

set mapreduce.input.fileinputformat.split.maxsize=256000000;

Reduce端并行度

Reduce端的并行度,可由用户自己指定,也可由Hive自行根据该MR Job输入的文件大小进行估算。

Reduce端的并行度的相关参数如下:

--指定Reduce端并行度,默认值为-1,表示用户未指定

set mapreduce.job.reduces;

--Reduce端并行度最大值

set hive.exec.reducers.max;

--单个Reduce Task计算的数据量,用于估算Reduce并行度

set hive.exec.reducers.bytes.per.reducer;

Reduce端并行度的确定逻辑如下:

若指定参数mapreduce.job.reduces的值为一个非负整数,则Reduce并行度为指定值。否则,Hive自行估算Reduce并行度,估算逻辑如下:

假设Job输入的文件大小为totalInputBytes

参数hive.exec.reducers.bytes.per.reducer的值为bytesPerReducer。

参数hive.exec.reducers.max的值为maxReducers。

则Reduce端的并行度为:

根据上述描述,可以看出,Hive自行估算Reduce并行度时,是以整个MR Job输入的文件大小作为依据的。因此,在某些情况下其估计的并行度很可能并不准确,此时就需要用户根据实际情况来指定Reduce并行度了。 

在默认情况下,是会进行map-side聚合的,也就是Reduce端接收的数据,实际上是map端完成聚合之后的结果。观察任务的执行过程,会发现,每个map端输出的数据只有34条记录,共有5个map task。

也就是说Reduce端实际只会接收170(34*5)条记录,故理论上Reduce端并行度设置为1就足够了。这种情况下,用户可通过以下参数,自行设置Reduce端并行度为1,这样把5个文件合并为只输出1个文件。

--指定Reduce端并行度,默认值为-1,表示用户未指定

set mapreduce.job.reduces=1;

小文件合并优化

Map端输入的小文件合并,和Reduce端输出的小文件合并。

合并Map端输入的小文件

将多个小文件划分到一个切片中,进而由一个Map Task去处理。目的是防止为单个小文件启动一个Map Task,浪费计算资源。

相关参数为:

--可将多个小文件切片,合并为一个切片,进而由一个map任务处理(默认)

set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

合并Reduce端输出的小文件

将多个小文件合并成大文件。目的是减少HDFS小文件数量。其原理是根据计算任务输出文件的平均大小进行判断,若符合条件,则单独启动1个额外的任务进行合并。

相关参数为:

--开启合并map only任务输出的小文件,默认false

set hive.merge.mapfiles=true;

--开启合并map reduce任务输出的小文件,默认false

set hive.merge.mapredfiles=true;

--合并后的文件大小

set hive.merge.size.per.task=256000000;

--触发小文件合并任务的阈值,若某计算任务输出的文件平均大小低于该值,则触发合并

set hive.merge.smallfiles.avgsize=16000000;

若reduce端设置并行度为5,则输出5个文件。下图为输出文件,可以看出,5个均为小文件: 

要避免5个小文件产生,可以设置reduce端并行度为1,有几个reduce并行就有几个文件产生,保证其输出结果只有一个文件或启用hive合并小文件优化

启用Hive合并小文件优化

设置以下参数:

--开启合并map reduce任务输出的小文件

set hive.merge.mapredfiles=true;

--合并后的文件大小

set hive.merge.size.per.task=256000000;

--触发小文件合并任务的阈值,若某计算任务输出的文件平均大小低于该值,则触发合并

set hive.merge.smallfiles.avgsize=16000000;

这样输出文件就合并为一个了

其他优化

CBO优化

CBO是指Cost based Optimizer,即基于计算成本的优化。

在Hive中,计算成本模型考虑到了:数据的行数、CPU、本地IO、HDFS IO、网络IO等方面。Hive会计算同一SQL语句的不同执行计划的计算成本,并选出成本最低的执行计划。目前CBO在hive的MR引擎下主要用于join的优化,例如多表join的join顺序。

相关参数为:

--是否启用cbo优化 

set hive.cbo.enable=true;

1)示例HQL

hive (default)> select from order_detail od

join product_info product on od.product_id=product.id

join province_info province on od.province_id=province.id;

2)关闭CBO优化

--关闭cbo优化 

set hive.cbo.enable=false;

--为了测试效果更加直观,关闭map join自动转换

set hive.auto.convert.join=false;

根据执行计划,可以看出,三张表的join顺序如下:

3)开启CBO优化

--开启cbo优化 

set hive.cbo.enable=true;

--为了测试效果更加直观,关闭map join自动转换

set hive.auto.convert.join=false;

根据执行计划,可以看出,三张表的join顺序如下:

CBO优化对于执行计划中join顺序是有影响的,其之所以会将province_info的join顺序提前,是因为province info的数据量较小,将其提前,会有更大的概率使得中间结果的数据量变小,从而使整个计算任务的数据量减小,也就是使计算成本变小。

谓词下推

谓词下推(predicate pushdown)是指,尽量将过滤操作前移,以减少后续计算步骤的数据量。

相关参数为:

--是否启动谓词下推predicate pushdown)优化

set hive.optimize.ppd = true;

需要注意的是:CBO优化也会完成一部分的谓词下推优化工作,因为在执行计划中,谓词越靠前,整个计划的计算成本就会越低。

矢量化查询

Hive的矢量化查询优化,依赖于CPU的矢量化计算,CPU的矢量化计算的基本原理如下图:

相关参数如下:

set hive.vectorized.execution.enabled=true;

若执行计划中,出现“Execution mode: vectorized”字样,即表明使用了矢量化计算。

Fetch抓取

Fetch抓取是指,Hive中对某些情况的查询可以不必使用MapReduce计算。例如:select * from emp;在这种情况下,Hive可以简单地读取emp对应的存储目录下的文件,然后输出查询结果到控制台。

相关参数如下:

--是否在特定场景转换为fetch 任务

--设置为none表示不转换

--设置为minimal表示支持select *,分区字段过滤,Limit等

--设置为more表示支持select 任意字段,包括函数,过滤,和limit等

set hive.fetch.task.conversion=more;

本地模式

大多数的Hadoop Job是需要Hadoop提供的完整的可扩展性来处理大数据集的。不过,有时Hive的输入数据量是非常小的。在这种情况下,为查询触发执行任务消耗的时间可能会比实际job的执行时间要多的多。对于大多数这种情况,Hive可以通过本地模式在单台机器上处理所有的任务,不必提交到Yarn。对于小数据集,执行时间可以明显被缩短。

相关参数如下:

--开启自动转换为本地模式

set hive.exec.mode.local.auto=true; 

--设置local MapReduce的最大输入数据量,当输入数据量小于这个值时采用local  MapReduce的方式,默认为134217728,即128M

set hive.exec.mode.local.auto.inputbytes.max=50000000;

--设置local MapReduce的最大输入文件个数,当输入文件个数小于这个值时采用local MapReduce的方式,默认为4

set hive.exec.mode.local.auto.input.files.max=10;

并行执行

Hive会将一个SQL语句转化成一个或者多个Stage,每个Stage对应一个MR Job。默认情况下,Hive同时只会执行一个Stage。但是某SQL语句可能会包含多个Stage,但这多个Stage可能并非完全互相依赖,也就是说有些Stage是可以并行执行的。此处提到的并行执行就是指这些Stage的并行执行。

相关参数如下:

--启用并行执行优化

set hive.exec.parallel=true;      

--同一个sql允许最大并行度,默认为8

set hive.exec.parallel.thread.number=8;

严格模式

Hive可以通过设置某些参数防止危险操作:

1)分区表不使用分区过滤

将hive.strict.checks.no.partition.filter设置为true时,对于分区表,除非where语句中含有分区字段过滤条件来限制范围否则不允许执行。换句话说,就是用户不允许扫描所有分区。进行这个限制的原因是,通常分区表都拥有非常大的数据集,而且数据增加迅速。没有进行分区限制的查询可能会消耗令人不可接受的巨大资源来处理这个表。

2)使用order by没有limit过滤

将hive.strict.checks.orderby.no.limit设置为true时,对于使用了order by语句的查询,要求必须使用limit语句。因为order by为了执行排序过程会将所有的结果数据分发到同一个Reduce中进行处理,强制要求用户增加这个limit语句可以防止Reduce额外执行很长一段时间(开启了limit可以在数据进入到Reduce之前就减少一部分数据)。

3)笛卡尔积

将hive.strict.checks.cartesian.product设置为true时,会限制笛卡尔积的查询。对关系型数据库非常了解的用户可能期望在执行JOIN查询的时候不使用ON语句而是使用where语句,这样关系数据库的执行优化器就可以高效地将WHERE语句转化成那个ON语句。不幸的是,Hive并不会执行这种优化,因此,如果表足够大,那么这个查询就会出现不可控的情况。

  • 30
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值