【Spark精讲】Spark on Hive性能优化

目录

第一章

[1.1 集群配置概述](#1.1 集群配置概述)

[1.2 集群规划概述](#1.2 集群规划概述)

[第二章 Yarn配置](#第二章 Yarn配置)

[2.1 Yarn配置说明](#2.1 Yarn配置说明)

yarn.nodemanager.resource.memory-mb

yarn.nodemanager.resource.cpu-vcores

yarn.scheduler.maximum-allocation-mb

yarn.scheduler.minimum-allocation-mb

[第三章 Spark的配置说明](#第三章 Spark的配置说明)

[3.1 Executor配置说明](#3.1 Executor配置说明)

[3.1.1 Executor CPU核数配置](#3.1.1 Executor CPU核数配置)

[3.1.2 Executor内存配置](#3.1.2 Executor内存配置)

[3.1.3 Executor个数配置](#3.1.3 Executor个数配置)

静态分配

动态分配

[3.2 Driver的配置](#3.2 Driver的配置)

[3.3 Spark配置实操](#3.3 Spark配置实操)

修改spark-defaults.conf文件

[配置Spark Shuffle服务](#配置Spark Shuffle服务)

[第四章 Hive SQL执行计划](#第四章 Hive SQL执行计划)

启动进程

[⭐️第五章 分组聚合优化](#⭐️第五章 分组聚合优化)

[5.1 优化前执行计划](#5.1 优化前执行计划)

实际运行

[5.2 优化思路](#5.2 优化思路)

[5.3 优化后执行计划](#5.3 优化后执行计划)

实际运行

[第六章 Join优化](#第六章 Join优化)

[6.1 Hive Join算法概述](#6.1 Hive Join算法概述)

[Common Join](#Common Join)

[Map Join](#Map Join)

[Sort Merge Bucket Map Join](#Sort Merge Bucket Map Join)

[6.2 Map Join优化](#6.2 Map Join优化)

[6.3 Sort Merge Bucket Map Join](#6.3 Sort Merge Bucket Map Join)

优化前

优化后

[第七章 数据倾斜优化](#第七章 数据倾斜优化)

[7.1 数据倾斜说明](#7.1 数据倾斜说明)

[7.2 分组聚合导致的数据倾斜](#7.2 分组聚合导致的数据倾斜)

[7.2.2 优化思路](#7.2.2 优化思路)

启用map-side聚合

[启用skew groupby优化](#启用skew groupby优化)

[7.3 join导致的数据倾斜](#7.3 join导致的数据倾斜)

[启用Skew Join优化](#启用Skew Join优化)

实操

[join 倾斜优化1 map join](#join 倾斜优化1 map join)

[join 倾斜优化2 skew join](#join 倾斜优化2 skew join)

[第八章 任务并行度优化](#第八章 任务并行度优化)

[8.1 优化说明](#8.1 优化说明)

[8.2 Map阶段并行度](#8.2 Map阶段并行度)

[8.3 Reduce阶段并行度](#8.3 Reduce阶段并行度)

[第九章 小文件合并优化](#第九章 小文件合并优化)

[9.1 优化说明](#9.1 优化说明)

[9.2 Map端输入小文件合并](#9.2 Map端输入小文件合并)

[9.3 Reduce输出小文件合并](#9.3 Reduce输出小文件合并)

其他优化


第一章

1.1 集群配置概述

5台节点,2台master节点,用于部署HDFS的NameNode,Yarn的ResourceManager等角色

3台worker节点,用于部署HDFS的DataNode,Yarn的NodeManager等角色

  • Master节点配置:16核,64GB
  • Worker阶段配置:32核,128GB

1.2 集群规划概述

HA模式:高可用集群

第二章 Yarn配置

2.1 Yarn配置说明

yarn.nodemanager.resource.memory-mb

一个NodeManager节点分配给Container使用的内存。

该参数的配置,取决于NodeManager所在节点的总内存容量和该节点运行的其他服务的数量。

设置为64GB,即 65535 MB。

free -m

查看空闲内存

yarn.nodemanager.resource.cpu-vcores

一个NodeManager节点分配给Container使用的CPU核数,

该参数的配置,取决于NodeManager所在节点的总CPU核数和该节点运行的其他服务。

设置为16。

yarn.scheduler.maximum-allocation-mb

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

设置为16GB,即16384MB。

yarn.scheduler.minimum-allocation-mb

单个Container能够使用的最小内存

设置为512MB。

修改NodeManager节点上的yarn-site.xml

改为后重启yarn进程,查看配置

ip:8088/cluster

第三章 Spark的配置说明

3.1 Executor配置说明

3.1.1 Executor 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 Executor内存配置

Spark在Yarn模式下的Executor内存模型如下图所示:

Excutor相关的参数有:spark.executor.memeory和spark.executor.memeoryOverhead

spark.executor.memeory用于指定Executor进程的堆内存大小,这部分内存用于任务的计算和存储;

spark.executor.memeoryOverhead用于指定Executor进程的堆外内存,这部分内存用于JVM的额外开销,操作系统的开销等。两者的和才算一个Executor进程所需要的总内存大小。默认情况下spark.executor.memeoryOverhead的值等于spark.executor.memeory*0.1。

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

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

64 ➗ 4 = 16GB

spark.executor.memeory 14GB

spark.executor.memeoryOverhead 2GB

3.1.3 Executor个数配置

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

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

静态分配

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

动态分配

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

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

#启动动态分配

spark.dynamicAllocation.enabled true

#启用Spark shuffle服务

spark.shuffle.service.enabled true

#spark shuffle老版本协议

spark.shuffle.useOldFetchProtocol true

#Executor 个数初始值

spark.dynamicAllocation.initialExecutors 1

#Executor 个数最小值

spark.dynamicAllocation.minExecutores 1

#Executor 个数最大值

spark.dynamicAllocation.maxExecutores 12

#Executor 空闲时长,若某Executor空闲时间超过此值,则会被关闭

spark.dynamicAllocation.executorIdleTimeout 60s

#积压任务等待时长,若有Task等待时间超过此值,则申请启动新的Executor

spark.dynamicAllocation.schedulerBacklogTimeout 1s(可调高到10s)

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

3.2 Driver的配置

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

spark.driver.memeory用于指定Driver进程的堆内存大小,

spark.driver.memoryOverhead用于指定Driver进程的堆外内存大小。默认情况下,两者的关系如下:

spark.driver.memoryOverhead=spark.driver.memeory*0.1

两者的和才算一个Driver进程所需要的总内存大小。

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

  • 若X > 50G,则Driver可设置为12G
  • 若12G < X < 50G,则Driver可设置为4G
  • 若1G < X < 12G,则Driver可设置为1G

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

spark.driver.memory 10G

spark.yarn.driver.memoryOverhead 2G

3.3 Spark配置实操

修改spark-defaults.conf文件

修改$HIVE_HOME/conf/spark-defautls.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.shuffle.service.enabled true

spark.shuffle.useOldFetchProtocol true

spark.dynamicAllocation.eanbled true

spark.dynamicAllocation.executorIdleTimeout 60s

spark.dynamicAllocation.initialExecutors 1

spark.dynamicAllocation.minExecutors 1

spark.dynamicAllocation.maxExecutors 11 (12减掉1个driver)

spark.dynamicAllocation.schedulerBacklogTimeout 1s(可调高到10s)

修改hadoop100节点的配置即可,也可以5台都配置,因为HiveServer2在hadoop100节点,连哪台哪台生效

配置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文件

<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle,spark_shuffle</value>
</property>

<property>
<name>yarn.nodemanager.aux-services.spark_shuffle.class</name>
<value>org.apache.spark.network.yarn.YarnShuffleService</value>
</property>

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

5.重启Yarn

第四章 Hive SQL执行计划

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

Expalin里呈现的执行计划,由一系列的Stage组成,这个Stage具有依赖关系,每个Stage对应一个MapReduce Job或者Spark Job,或者一个文件系统操作等。

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

解析器(检查 语法的正确性)->编译器(生产逻辑执行计划)->优化器(优化后的逻辑执行计划)->

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

stage 1

operator 1

operator 2

stage 2

operator 1

operator 2

启动进程

start-hdfs.sh

start-yarn.sh

nohup hiveserver2 &

⭐️第五章 分组聚合优化

SELECT
	coupon_id,
  count(1)
FROM
	dwd_trade_order_detail_inc
WHERE
	dt = '20230-06-16'
GROUP BY
	coupon_id;

40G大小,加载到内存有160G左右

5.1 优化前执行计划

实际运行

运行时间:54秒

5.2 优化思路

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

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

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

--hash map占用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.agggr.hash.force.flush.memory.treshold=0.9;

5.3 优化后执行计划

实际运行

优化后8秒~20秒

第六章 Join优化

6.1 Hive Join算法概述

Hive拥有多种join算法,包括:Common Join,Map Join, Sort Merge Bucket Map Join等。

Common Join

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

Map Join

若参与的Join表中,有n-1张足够小,Map端就会缓存小表全部数据,然后扫描另外一张大表,在Map端完成关联操作。

Sort Merge Bucket Map Join

若参与join的表均为分桶表,切关联字段为分桶字段,且分桶字段是有序的,且大表分桶数量是小表分桶数量的整数倍。此时,就可以以分桶为单位,为每个Map分配任务了,Map端就无需再缓存小表的全部数据了,而只需缓存其需的分桶。

比如,用户表12G,用户订单表120G,这两个进行Join时可以采用该优化手段

6.2 Map Join优化

Map Join相关参数

--启用map join自动转换,默认为true
set hive.auto.convert.join = true

--common join 转 map join小表阈值,10MB
set hive.auto.convert.join.noconditionaltask.size = 10000000

可以通过以下命令获取表的大小

desc formatted table_name;

参看rawDataSize

6.3 Sort Merge Bucket Map Join

示例SQL

SELECT
	*
 FROM (
   SELECT
     *
   FROM dim_user_zip --12GB
   WHERE
     dt = '9999-12-31'
 ) duz
 JOIN (
  	SELECT
     *
    FROM
     dwd_trade_order_detail_inc --160GB
    WHERE
     dt = '2020-06-16'
 ) dtodi
 ON duz.user_id = dtodi.user_id;
优化前

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

优化前运行:230秒(3分钟35秒)

优化后

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

|----------------------------|-------|
| 表名 | 大小 |
| dwd_trade_order_detail_inc | 160GB |
| dim_usr_zip | 12GB |

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

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

--订单明细表
dorp 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
  ....
) clustered by (user_id) 
sorted by (user_id) into 36 buckets
row format delimited fields terminated by '\t';

--用户表
dorp table if exists dim_usr_zip_bucketed;
create table dim_usr_zip_bucketed (
  id string,
  login_name string,
  nick_name string,
  name string
  ...
) clustered by (id) 
sorted by (id) into 6 buckets
row format delimited fields terminated by '\t';

然后向两张分桶表导入数据

--订单明细表
insert overwrite table dwd_trade_order_detail_inc_bucketed
select
	id,
  order_id,
  user_id,
  ...
from
	dwd_trade_order_detail_inc
where
	dt = '2020-06-16';

 
--用户表
insert overwrite table dim_usr_zip_bucketed
select
	id,
  login_name,
  nick_name,
  ...
from
	dim_usr_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_usr_zip_bucketed duser
on 
	od.user_id = duser.id;

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

实际运行:100秒

第七章 数据倾斜优化

7.1 数据倾斜说明

数据往往都是倾斜的

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

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

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

select
	provice_id,
  count(*)
from
	dwd_trade_order_detail_inc
where
	dt = '2020-06-16'
group by provice_id;--某一省份9900万条

7.2.2 优化思路

分组聚合导致的倾斜主要有两种优化思路

启用map-side聚合

相关参数如下

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

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

启用skew groupby优化

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

相关参数如下:

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

--实际测试时注意把map-side聚合关闭
set hive.map.aggr=false;

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

实际运行34秒,提升60%

总结:优选map-side聚合,执行步骤少,更优

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.provice_id = dim.id;

启用Skew Join优化

map join适用于小表,skew join适用于大表

map join参考:《浅谈Hive中Map Join原理及场景》《hive之Map Join使用方法》

相关参数如下:

--启用skew join优化
set hive.optimize.skewjoin=true;
--触发skew join的阈值,若某个key的行数超过该参数值,则触发
set hive.skewjoin.key=100000;
--某个key多少行数据必须在实际运行中进行统计才行,不像mapjoin运行前已经知道

需要注意的是,skew join只支持inner join

实操

优化前,确保以下设置是关闭的,实际运行24分钟

--关闭map join
set hive.auto.convert.join=false;
--关闭skew join
set hive.optimize.skewjoin=false;
join 倾斜优化1 map join

优化前,确保以下设置是关闭的,实际运行1分钟30秒

--开启map join
set hive.auto.convert.join=true;
--关闭skew join
set hive.optimize.skewjoin=false;
join 倾斜优化2 skew join

优化前,确保以下设置是关闭的,实际运行19分钟25秒

--关闭map join
set hive.auto.convert.join=false;
--开启skew join
set hive.optimize.skewjoin=true;

第八章 任务并行度优化

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端并行度,最大值,默认值1009
set hive.exec.reducers.max;
--单个Reduce Task计算的数据量,用于估算Reduce并行度,默认值256MB
set hive.exec.reducers.bytes.per.reducer;

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

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

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

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

则Reducer端的并行度为

乘以2是因为Spark尽可能让Reduce多一点

explain时由于没有真正执行map,所以是那map的输入数据量来估算的。需要增加参数来让估算变准。

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

--执行DML语句时,收集表基本的统计信息,默认为true
set hive.stats.autogather=true;

--执行DML语句时,收集字段基本的统计信息,默认为true
set hive.stats.column.autogather=true;

--计算Reduce并行度时,从上游Operator统计信息获得输入数据量,默认为true
set hive.spark.use.op.stats=true;

--计算Reduce并行度时,从上游Operator统计信息获得输入数据量,默认为false
set hive.spark.fetch.column.stats=true;

第九章 小文件合并优化

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;

其他优化

参考:

相关推荐
PersistJiao1 小时前
Spark RDD中常用聚合算子源码层面的对比分析
spark·源码分析·rdd·聚合算子
小_太_阳1 小时前
hadoop_yarn详解
大数据·hadoop·yarn
Data-Miner2 小时前
大数据湖项目建设方案(100页WORD)
大数据·big data
AI服务老曹3 小时前
不仅能够实现前后场的简单互动,而且能够实现人机结合,最终实现整个巡检流程的标准化的智慧园区开源了
大数据·人工智能·深度学习·物联网·开源
管理大亨4 小时前
大数据微服务方案
大数据
脸ル粉嘟嘟5 小时前
大数据CDP集群中Impala&Hive常见使用语法
大数据·hive·hadoop
宝哥大数据5 小时前
数据仓库面试题集&离线&实时
大数据·数据仓库·spark
八荒被注册了5 小时前
6.584-Lab1:MapReduce
大数据·mapreduce
寰宇视讯5 小时前
“津彩嘉年,洽通天下” 2024中国天津投资贸易洽谈会火热启动 首届津彩生活嘉年华重磅来袭!
大数据·人工智能·生活
刘艳兵的学习博客5 小时前
刘艳兵-DBA033-如下那种应用场景符合Oracle ROWID存储规则?
服务器·数据库·oracle·面试·刘艳兵