hive on spark调优学习笔记

1 集群环境概述

1.1 集群配置概述:

所用集群由5台节点构成,其中2台为master节点,用于部署HDFS的NameNode,Yarn的ResourceManager等角色,另外3台为worker节点,用于部署HDFS的DataNode、Yarn的NodeManager等角色。

Master节点配置为16核CPU、64G内存。

Workder节点配置为32核CPU、128G内存。

1.2 集群规划概述:

hive on spark调优学习笔记_第1张图片

 2 yarn配置

2.1 yarn配置说明

需要调整的yarn参数均与CPU、内存等资源有关,核心配置参数如下:

(1)yarn.nodemanager.resource.memory-mb

该参数的含义是,一个NodeManager节点分配给Container使用的内存。该参数的配置,取决于NodeManager所在节点的总内存容量和该节点运行的其他服务的数量。考虑上述因素,此处可将该参数设置为64G。


    yarn.nodemanager.resource.memory-mb
    65536

(2)yarn.nodemanager.resource.cpu-vcores

)yarn.nodemanager.resource.cpu-vcores

该参数的含义是,一个NodeManager节点分配给Container使用的CPU核数。该参数的配置,同样取决于NodeManager所在节点的总CPU核数和该节点运行的其他服务。考虑上述因素,此处可将该参数设置为16。


    yarn.nodemanager.resource.cpu-vcores
    16

(3)yarn.scheduler.maximum-allocation-mb

该参数的含义是,单个Container能够使用的最大内存。由于Spark的yarn模式下,Driver和Executor都运行在Container中,故该参数不能小于Driver和Executor的内存配置,推荐配置如下:


    yarn.scheduler.maximum-allocation-mb
    16384

(4)yarn.scheduler.minimum-allocation-mb

该参数的含义是,单个Container能够使用的最小内存,推荐配置如下:


    yarn.scheduler.minimum-allocation-mb
    512

2.2 yarn配置实操

查看内存:

 进入配置文件:102,103,104需要修改,100,101不需要nodemanger。102改完之后需要分发给103,104然后重启下yarn进程。

hive on spark调优学习笔记_第2张图片

(1)修改$HADOOP_HOME/etc/hadoop/yarn-site.xml文件

(2)修改如下参数


    yarn.nodemanager.resource.memory-mb
    65536


    yarn.nodemanager.resource.cpu-vcores
    16


    yarn.scheduler.maximum-allocation-mb
    16384


    yarn.scheduler.minimum-allocation-mb
    512

(3)分发该配置文件

(4)重启Yarn。

 3 spark配置

Driver相当于一个应用程序的管理者,Excutor是正式工作的进程。Driver只能由一个,Excutor可以有多个。Driver负责给各个Excutor申请资源,并且监控Excutor是否执行完毕,以及一些状态管理,Excutor是"真正干活的人",所以配置的时候主要是以Excutor为主。配置要基于前面的yarn,比如一个Nodemanger内存64G,一个Container内存16G,所以Driver和Excutor的配置不能随心所欲。

3.1 Executor配置说明

3.1.1 Excutor cpu核数配置

单个Executor的CPU核数,由spark.executor.cores参数决定,建议配置为4-6,具体配置为多少,视具体情况而定,原则是尽量充分利用资源。

此处单个节点共有16个核可供Executor使用,则spark.executor.core配置为4最合适。原因是,若配置为5,则单个节点只能启动3个Executor,会剩余1个核未使用;若配置为6,则只能启动2个Executor,会剩余4个核未使用。

3.1.2 Excutor内存配置

spark在yarn模式下的excutor内存模型如下图

hive on spark调优学习笔记_第3张图片

Executor相关的参数有:spark.executor.memory和spark.executor.memoryOverhead。spark.executor.memory用于指定Executor进程的堆内存大小,这部分内存用于任务的计算和存储;spark.executor.memoryOverhead用于指定Executor进程的堆外内存,这部分内存用于JVM的额外开销,操作系统开销等。两者的和才算一个Executor进程所需的总内存大小。默认情况下spark.executor.memoryOverhead的值等于spark.executor.memory*0.1。

以上两个参数的推荐配置思路是,先按照单个NodeManager的核数和单个Executor的核数,计算出每个NodeManager最多能运行多少个Executor。在将NodeManager的总内存平均分配给每个Executor,最后再将单个Executor的内存按照大约10:1的比例分配到spark.executor.memory和spark.executor.memoryOverhead。

根据上述思路,可得到如下关系:

(spark.executor.memory+spark.executor.memoryOverhead)= yarn.nodemanager.resource.memory-mb * (spark.executor.cores/yarn.nodemanager.resource.cpu-vcores)

经计算,此处应做如下配置:

spark.executor.memory    14G
spark.executor.memoryOverhead    2G

3.1.3 Executor个数配置

此处的Executor个数是指分配给一个Spark应用的Executor个数,Executor个数对于Spark应用的执行速度有很大的影响,所以Executor个数的确定十分重要。

一个Spark应用的Executor个数的指定方式有两种,静态分配动态分配

1)静态分配

可通过spark.executor.instances指定一个Spark应用启动的Executor个数。这种方式需要自行估计每个Spark应用所需的资源,并为每个应用单独配置Executor个数。

2)动态分配

    动态分配可根据一个Spark应用的工作负载,动态的调整其所占用的资源(Executor个数)。这意味着一个Spark应用程序可以在运行的过程中,需要时,申请更多的资源(启动更多的Executor),不用时,便将其释放。

在生产集群中,推荐使用动态分配。动态分配相关参数如下:

#启动动态分配
spark.dynamicAllocation.enabled    true
#启用Spark shuffle服务
spark.shuffle.service.enabled    true
#Executor个数初始值
spark.dynamicAllocation.initialExecutors    1
#Executor个数最小值
spark.dynamicAllocation.minExecutors    1
#Executor个数最大值
spark.dynamicAllocation.maxExecutors    12
#Executor空闲时长,若某Executor空闲时间超过此值,则会被关闭
spark.dynamicAllocation.executorIdleTimeout    60s
#积压任务等待时长,若有Task等待时间超过此值,则申请启动新的Executor
spark.dynamicAllocation.schedulerBacklogTimeout    1s
#spark shuffle老版本协议
spark.shuffle.useOldFetchProtocol true

说明:Spark shuffle服务的作用是管理Executor中的各Task的输出文件,把文件放在hdfs上,主要是shuffle过程map端的输出文件。由于启用资源动态分配后,Spark会在一个应用未结束前,将已经完成任务,处于空闲状态的Executor关闭。Executor关闭后,其输出的文件,也就无法供其他Executor使用了。需要启用Spark shuffle服务,来管理各Executor输出的文件,这样就能关闭空闲的Executor,而不影响后续的计算任务了。

3.2 Driver配置说明

Driver主要配置内存即可,相关的参数有spark.driver.memory和spark.driver.memoryOverhead。

spark.driver.memory用于指定Driver进程的堆内存大小,spark.driver.memoryOverhead用于指定Driver进程的堆外内存大小。默认情况下,两者的关系如下:spark.driver.memoryOverhead=spark.driver.memory*0.1。两者的和才算一个Driver进程所需的总内存大小。

一般情况下,按照如下经验进行调整即可:假定yarn.nodemanager.resource.memory-mb设置为X,

若X>50G,则Driver可设置为12G,

若12G

若1G

此处yarn.nodemanager.resource.memory-mb为64G,则Driver的总内存可分配12G,所以上述两个参数可配置为。

spark.driver.memory    10G
spark.yarn.driver.memoryOverhead    2G

3.3spark配置实操

hive on spark调优学习笔记_第4张图片

 3.3.1 修改spark-defaults.conf文件

 修改$HIVE_HOME/conf/spark-defaults.conf

spark.master                               yarn
spark.eventLog.enabled                   true
spark.eventLog.dir    hdfs://myNameService1/spark-history
spark.executor.cores    4
spark.executor.memory    14g
spark.executor.memoryOverhead    2g
spark.driver.memory    10g
spark.driver.memoryOverhead    2g
spark.dynamicAllocation.enabled  true
spark.shuffle.service.enabled  true
spark.dynamicAllocation.executorIdleTimeout  60s
spark.dynamicAllocation.initialExecutors    1
spark.dynamicAllocation.minExecutors  1
spark.dynamicAllocation.maxExecutors  12
spark.dynamicAllocation.schedulerBacklogTimeout 1s
spark.shuffle.useOldFetchProtocol    true

3.3.2 配置Spark shuffle服务

Spark Shuffle服务的配置因Cluster Manager(standalone、Mesos、Yarn)的不同而不同。此处以Yarn作为Cluster Manager。

(1)拷贝$SPARK_HOME/yarn/spark-3.0.0-yarn-shuffle.jar到

$HADOOP_HOME/share/hadoop/yarn/lib

(2)分发$HADOOP_HOME/share/hadoop/yarn/lib/yarn/spark-3.0.0-yarn-shuffle.jar

(3)修改$HADOOP_HOME/etc/hadoop/yarn-site.xml文件


    yarn.nodemanager.aux-services
    mapreduce_shuffle,spark_shuffle



    yarn.nodemanager.aux-services.spark_shuffle.class
    org.apache.spark.network.yarn.YarnShuffleService

(4)分发$HADOOP_HOME/etc/hadoop/yarn-site.xml文件

(5)重启Yarn

4 Hive SQL执行计划

Hive SQL的执行计划,可由Explain查看。

Explain呈现的执行计划,由一系列Stage组成(不是spark里根据宽窄依赖划分的stage),这个Stage具有依赖关系,每个Stage对应一个MapReduce Job或者Spark Job,或者一个文件系统操作等。

每个Stage由一系列的Operator组成,一个Operator代表一个逻辑操作,例如TableScan Operator,Select Operator,Join Operator等。

Stage与Operator的对应关系如下图:

hive on spark调优学习笔记_第5张图片

explain资料: 

https://cwiki.apache.org/confluence/download/attachments/44302539/hos_explain.pdf?version=1&modificationDate=1425575903211&api=v2

5 分组聚合优化

前言:如果想查看一个表的大小等信息,第一个笨办法就是去hdfs上查看,也可以用下面的命令。

-- desc formatted xxx 例如:
desc formatted dwd_trade_order_detail_inc

hive on spark调优学习笔记_第6张图片

 numFiles:文件数量。numPartitions:分区数量。numRows:行数。orc.compress代表文件本身是orc类型并且用了snappy压缩。totalSize代表文件经过orc存储和snappy压缩后的大小(4g)。rawDataSize代表行存并且没有解压缩的大小(160g)。rawDataSize是最后要读取的大小。

 示例sql语句:

select
    coupon_id,
    count(*)
from dwd_trade_order_detail_inc
where dt='2020-06-16'
group by coupon_id;

5.1 优化前执行计划

hive on spark调优学习笔记_第7张图片

 5.2 优化思路

优化思路为map-side聚合,预聚合。所谓map-side聚合,就是在map端维护一个hash table,利用其完成分区内的、部分的聚合(自己分区的),然后将部分聚合的结果,发送至reduce端,完成最终的聚合。map-side聚合能有效减少shuffle的数据量,提高分组聚合运算的效率。

原因有两点:1、预聚合后shuffle的数据量减少

                     2、在map阶段进行计算更好,因为reduce除了计算还要涉及到数据的存储

map-side聚合相关的参数如下:

--启用map-side聚合
set hive.map.aggr=true;

--hash table占用map端内存的最大比例,超过比例会落盘一次
set hive.map.aggr.hash.percentmemory=0.5;

--用于检测源表是否适合map-side聚合的条数。
set hive.groupby.mapaggr.checkinterval=100000;

--map-side聚合所用的HashTable,占用map任务堆内存的最大比例,若超出该值,则会对HashTable进行一次flush。
set hive.map.aggr.hash.force.flush.memory.threshold=0.9;

 这里的0.9是0.5之后的0.9。如果给map端内存1G,则hash table最多500M,那么超过500*0.9=450M就会进行一次hash table的flush。目的是留一点缓冲空间。

5.3 优化后执行计划

hive on spark调优学习笔记_第8张图片

 6 Join优化

 6.1 Hive Join算法概述

Hive拥有多种join算法,包括common join,map join,sort Merge Bucket Map Join等。下面对每种join算法做简要说明:

1)common join

Map端负责读取参与join的表的数据,并按照关联字段进行分区,将其发送到Reduce端,Reduce端完成最终的关联操作。

hive on spark调优学习笔记_第9张图片

2)map join

若参与join的表中,有n-1张表足够小,Map端就会缓存小表全部数据,加载到内存中,然后扫描另外一张大表,在Map端完成关联操作。整个任务没有reduce阶段。

hive on spark调优学习笔记_第10张图片

3)Sort Merge Bucket Map Join

大表join大表的情况可采用,分桶之后可以变成大表join小表走map join。若参与join的表均为分桶表,且关联字段为分桶字段,且分桶字段是有序的,且大表的分桶数量是小表分桶数量的整数倍。此时,就可以以分桶为单位,为每个Map分配任务了,Map端就无需再缓存小表的全表数据了,而只需缓存其所需的分桶。

大表的分桶数量是小表粪桶数量的整数倍:

例如上图,大表0,4的桶的id在小表0桶里都有。依次1、5->1,2、6->3,3、7->3 

另外还需要进行桶内排序:

 

原因是如果不排序会是全扫描,排序后可以做到全量IO变为部分IO。 

6.2 Map Join优化

示例SQL语句如下:

select
    *
from
(
    select
        *
    from dwd_trade_order_detail_inc
    where dt='2020-06-16'
)fact
left join
(
    select
        *
    from dim_sku_full
    where dt='2020-06-16'
)dim
on fact.sku_id=dim.id;

6.2.1 优化前执行计划

hive on spark调优学习笔记_第11张图片6.2.2 优化思路

上述参与join的两表一大一小,可考虑map join优化。

Map Join相关参数如下:

--启用map join自动转换
set hive.auto.convert.join=true;
--common join转map join小表阈值
set hive.auto.convert.join.noconditionaltask.size

 6.2.3 优化后执行计划

hive on spark调优学习笔记_第12张图片

6.3 Sort Merge Bucket Map Join

6.3.1 优化说明 

Sort Merge Bucket Map Join相关参数:

--启动Sort Merge Bucket Map Join优化
set hive.optimize.bucketmapjoin.sortedmerge=true;
--使用自动转换SMB Join
set hive.auto.convert.sortmerge.join=true;

6.3.2 优化案例

1)示例SQL语句

select
    *
from(
    select
        *
from  dim_user_zip  
where dt='9999-12-31'
)duz
join(
    select
        *
    from dwd_trade_order_detail_inc
    where dt='2020-06-16'
)dtodi
on duz.id=dtodi.user_id;

2)优化前

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

3)优化思路

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

表名 大小
dwd_trade_order_detail_inc 162900000000(约160g)
dim_user_zip 12320000000 (约12g)

两张表都相对较大,可以考虑采用SMB,SMB Map Join对分桶大小是没有要求的。下面演示如何使用SMB Map Join。

首先需要依据源表创建两个的有序的分桶表,dwd_trade_order_detail_inc建议分36个bucket,dim_user_zip建议分6个bucket,注意分桶个数的倍数关系以及分桶字段和排序字段

--订单明细表
hive (default)> 
drop table if exists dwd_trade_order_detail_inc_bucketed;
create table dwd_trade_order_detail_inc_bucketed(
id string,
order_id string,
user_id string,
sku_id string,
province_id string,
activity_id string,
activity_rule_id string,
coupon_id string,
date_id string,
create_time string,
source_id string,
source_type_code string,
source_type_name string,
sku_num bigint,
split_original_amount decimal(16,2),
split_activity_amount decimal(16,2),
split_coupon_amount decimal(16,2),
split_total_amount decimal(16,2)
)
clustered by (user_id) sorted by(user_id) into 36 buckets
row format delimited fields terminated by '\t';

--用户表
hive (default)> 
drop table if exists dim_user_zip_bucketed;
create table dim_user_zip_bucketed(
id string,
login_name string,
nick_name string,
name string,
phone_num string,
email string,
user_level string,
birthday string,
gender string,
create_time string,
operate_time string,
start_date string,
end_date string,
dt string
)
clustered by (id) sorted by(id) into 6 buckets
row format delimited fields terminated by '\t';

然后向两个分桶表导入数据。

--订单明细分桶表
hive (default)> 
insert overwrite table dwd_trade_order_detail_inc_bucketed
select
id ,
order_id ,
user_id ,
sku_id ,
province_id ,
activity_id ,
activity_rule_id ,
coupon_id ,
date_id ,
create_time ,
source_id ,
source_type_code ,
source_type_name ,
sku_num ,
split_original_amount ,
split_activity_amount ,
split_coupon_amount,
split_total_amount
from  dwd_trade_order_detail_inc
where dt='2020-06-16';

--用户分桶表
hive (default)> 
insert overwrite table dim_user_zip_bucketed
select
id,
login_name,
nick_name,
name,
phone_num,
email,
user_level,
birthday,
gender,
create_time,
operate_time,
start_date,
end_date,
dt
from dim_user_zip
where dt='9999-12-31';

然后设置以下参数:

--启动Sort Merge Bucket Map Join优化
set hive.optimize.bucketmapjoin.sortedmerge=true;
--使用自动转换SMB Join
set hive.auto.convert.sortmerge.join=true;

最后再重写SQL语句,如下:

select
    *
from dwd_trade_order_detail_inc_bucketed od
join dim_user_zip_bucketed duser
on od.user_id = duser.id;

优化后的执行计如图所示:

hive on spark调优学习笔记_第13张图片

7 数据倾斜优化 

 7.1 数据倾斜说明

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

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

7.2 分组聚合导致的数据倾斜

示例SQL语句如下:

select
    province_id,
    count(*)
from dwd_trade_order_detail_inc
where dt='2020-06-16'
group by province_id;

7.2.1 优化前执行计划

hive on spark调优学习笔记_第14张图片

 7.2.2 优化思路

由分组聚合导致的数据倾斜问题主要有以下两种优化思路:

1)启用map-side聚合

相关参数如下:

--启用map-side聚合
set hive.map.aggr=true;
--hash map占用map端内存的最大比例
set hive.map.aggr.hash.percentmemory=0.5;

启用map-side聚合后的执行计划如下图所示

hive on spark调优学习笔记_第15张图片

2)启用skew groupby优化

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

相关参数如下:

--启用分组聚合数据倾斜优化
set hive.groupby.skewindata=true;

启用skew groupby优化后的执行计划如下图所示

hive on spark调优学习笔记_第16张图片

7.3 Join导致的数据倾斜 

 示例SQL语句如下。

select
    *
from
(
    select
        *
    from dwd_trade_order_detail_inc
    where dt='2020-06-16'
)fact
join
(
    select
        *
    from dim_province_full
    where dt='2020-06-16'
)dim
on fact.province_id=dim.id;

7.3.1 优化前的执行计划

hive on spark调优学习笔记_第17张图片

 7.3.2 优化思路

由join导致的数据倾斜问题主要有以下两种优化思路:

1)使用map join

相关参数如下:

--启用map join自动转换
set hive.auto.convert.join=true;
--common join转map join小表阈值
set hive.auto.convert.join.noconditionaltask.size

使用map join优化后执行计划如下图。

hive on spark调优学习笔记_第18张图片

2)启用skew join优化

小表没有那么小,达不到map join的条件。

其原理如下图

hive on spark调优学习笔记_第19张图片

相关参数如下:

--启用skew join优化
set hive.optimize.skewjoin=true;
--触发skew join的阈值,若某个key的行数超过该参数值,则触发
set hive.skewjoin.key=100000;

需要注意的是,skew join只支持Inner Join。

启动skew join优化后的执行计划如下图所示:

hive on spark调优学习笔记_第20张图片

 8 任务并行度优化

 8.1 优化说明

 对于一个分布式的计算任务而言,设置一个合适的并行度十分重要。在Hive中,无论其计算引擎是什么,所有的计算任务都可分为Map阶段和Reduce阶段。所以并行度的调整,也可从上述两个方面进行调整。

8.2 Map阶段并行度

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

--可将多个小文件切片,合并为一个切片,进而由一个map任务处理
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; 
--一个切片的最大值
set mapreduce.input.fileinputformat.split.maxsize=256000000;

8.3 Reduce阶段并行度

Reduce端的并行度,相对来说,更需要关注。默认情况下,Hive会根据Reduce端输入数据的大小,估算一个Reduce并行度。但是在某些情况下,其估计值不一定是最合适的,故需要人为调整其并行度。

Reduce并行度相关参数如下:

--指定Reduce端并行度,默认值为-1,表示用户未指定
set mapreduce.job.reduces;
--Reduce端并行度最大值
set hive.exec.reducers.max;
--单个Reduce Task计算的数据量,用于估算Reduce并行度
set hive.exec.reducers.bytes.per.reducer;

假设Reduce端输入的数据量大小为totalInputBytes

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

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

则Reduce端的并行度为:

min⁡(ceiltotalInputBytesbytesPerReducer,maxReducers)

其中,Reduce端输入的数据量大小,是从Reduce上游的Operator的Statistics(统计信息)中获取的。为保证Hive能获得准确的统计信息,需配置如下参数:

--执行DML语句时,收集表级别的统计信息
set hive.stats.autogather=true;
--执行DML语句时,收集字段级别的统计信息
set hive.stats.column.autogather=true;
--计算Reduce并行度时,从上游Operator统计信息获得输入数据量
set hive.spark.use.op.stats=true;
--计算Reduce并行度时,使用列级别的统计信息估算输入数据量
set hive.stats.fetch.column.stats=true;

 9 小文件合并优化

9.1 优化说明

小文件合并优化,分为两个方面,分别是Map端输入的小文件合并,和Reduce端输出的小文件合并。

9.2 Map端输入文件合并

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

相关参数为:

--可将多个小文件切片,合并为一个切片,进而由一个map任务处理
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

9.3 Reduce输出文件合并

合并Reduce端输出的小文件,是指将多个小文件合并成大文件。目的是减少HDFS小文件数量。

相关参数为:

--开启合并Hive on Spark任务输出的小文件
set hive.merge.sparkfiles=true;

你可能感兴趣的:(hive,spark,学习)