1.
分配更多的资源
1.1
分配哪些资源
1.2
在哪里可以设置这些资源
1.3
参数调节到多大,算是最大
分配更多的资源:
它是性能优化调优的王道,就是增加和分配更多的资源,这对于性能和速度上的提升是显而易见的,
基本上,在一定范围之内,增加资源与性能的提升,是成正比的;写完了一个复杂的
spark
作业之后,进行性能调
优的时候,首先第一步,就是要来调节最优的资源配置;在这个基础之上,如果说你的
spark
作业,能够分配的资源达到
了你的能力范围的顶端之后,无法再分配更多的资源了,公司资源有限;那么才是考虑去做后面的这些性能调优的点。
相关问题:
(
1
)分配哪些资源?
(
2
)在哪里可以设置这些资源?
(
3
)剖析为什么分配这些资源之后,性能可以得到提升?
executor‐memory
、
executor‐cores
、
driver‐memory
在实际的生产环境中,提交
spark
任务时,使用
spark‐submit shell
脚本,在里面调整对应的参数。
提交任务的脚本
:
spark‐submit \
‐‐master spark://node1:7077 \
‐‐class cn.itcast.WordCount \
‐‐num‐executors 3 \
配置
executor
的数量
‐‐driver‐memory 1g \
配置
driver
的内存(影响不大)
‐‐executor‐memory 1g \
配置每一个
executor
的内存大小
‐‐executor‐cores 3 \
配置每一个
executor
的
cpu
个数
/export/servers/wordcount.jar
1.4
为什么调大资源以后性能可以提升
2.
提高并行度
2.1 Spark
的并行度指的是什么
第一种情况:
standalone
模式
先计算出公司
spark
集群上的所有资源 每台节点的内存大小和
cpu
核数,
比如:一共有
20
台
worker
节点,每台节点
8g
内存,
10
个
cpu
。
实际任务在给定资源的时候,可以给
20
个
executor
、每个
executor
的内存
8g
、每个
executor
的使用的
cpu
个数
10
。
第二种情况:
Yarn
先计算出
yarn
集群的所有大小,比如一共
500g
内存,
100
个
cpu
;
这个时候可以分配的最大资源,比如给定
50
个
executor
、每个
executor
的内存大小
10g,
每个
executor
使用的
cpu
个数为
2
。
使用原则:你能使用的资源有多大,就尽量去调节到最大的大小(
executor
的数量:几十个到上百个不等;
executor
的
内存;
exector
的
cpu
个数)
2.2
如何提高并行度
2.2.1
可以设置
task
的数量
2.2.2
如何设置
task
数量来提高并行度
2.2.3
给
RDD
重新设置
partition
的数量
spark
作业中,各个
stage
的
task
的数量,也就代表了
spark
作业在各个阶段
stage
的并行度!
当分配完所能分配的最大资源了,然后对应资源去调节程序的并行度,如果并行度没有与资源相匹配,那么导致你
分配下去的资源都浪费掉了。同时并行运行,还可以让每个
task
要处理的数量变少(很简单的原理。合理设置并行度,
可以充分利用集群资源,减少每个
task
处理数据量,而增加性能加快运行速度。)
举例说明:
假如, 现在已经在
spark‐submit
脚本里面,给我们的
spark
作业分配了足够多的资源,比如
50
个
executor
,每
个
executor
有
10G
内存,每个
executor
有
3
个
cpu core
。 基本已经达到了
spark
集群或者
yarn
集群上限。
task
没有
设置,或者设置的很少,比如就设置了
100
个
task
、
50
个
executor
、每个
executor
有
3
个
core
,也就是说
Application
任何一个
stage
运行的时候,都有总数
150
个
cpu core
,可以并行运行。
但是你现在只有
100
个
task
,平均分配一下,每个
executor
分配到
2
个
task
,那么同时在运行的
task
,只有
100
个
task
,每个
executor
只会并行运行
2
个
task
。 每个
executor
剩下的一个
cpu core
就浪费掉了!你的资源,虽然分
配充足了,但是问题是, 并行度没有与资源相匹配,导致你分配下去的资源都浪费掉了。合理的并行度的设置,应该要
设置的足够大,大到可以完全合理的利用你的集群资源; 比如上面的例子,总共集群有
150
个
cpu core
,可以并行运
行
150
个
task
。那么你就应该将你的
Application
的并行度,至少设置成
150
个,才能完全有效的利用你的集群资源,
让
150
个
task
并行执行,而且
task
增加到
150
个以后,即可以同时并行运行,还可以让每个
task
要处理的数量变少; 比
如总共
150G
的数据要处理, 如果是
100
个
task
,每个
task
要计算
1.5G
的数据。 现在增加到
150
个
task
,每个
task
只
要处理
1G
数据。
至少设置成与
spark Application
的总
cpu core
数量相同(最理想情况,
150
个
core
,分配
150task
,一起运
行,差不多同一时间运行完毕)官方推荐,
task
数量,设置成
spark Application
总
cpu core
数量的
2~3
倍 。
比如
150
个
cpu core
,基本设置
task
数量为
300~500.
与理想情况不同的,有些
task
会运行快一点,比如
50s
就完
了,有些
task
可能会慢一点,要一分半才运行完,所以如果你的
task
数量,刚好设置的跟
cpu core
数量相同,可能会
导致资源的浪费。
因为比如
150
个
task
中
10
个先运行完了,剩余
140
个还在运行,但是这个时候,就有
10
个
cpu core
空闲出来了,导
致浪费。如果设置
2~3
倍,那么一个
task
运行完以后,另外一个
task
马上补上来,尽量让
cpu core
不要空闲。同时尽量
提升
spark
运行效率和速度。提升性能。
设置参数
spark.defalut.parallelism
默认是没有值的,如果设置了值为
10
,它会在
shuffle
的过程才会起作用。
比如
val rdd2 = rdd1.reduceByKey(_+_)
此时
rdd2
的分区数就是
10
,
rdd1
的分区数不受这个参数的影响。
可以通过在构建
SparkConf
对象的时候设置,例如:
new SparkConf().set("spark.defalut.parallelism","500")
使用
rdd.repartition
来重新分区,该方法会生成一个新的
rdd
,使其分区数变大。
此时由于一个
partition
对应一个
task
,那么对应的
task
个数越多,通过这种方式也可以提高并行度。
2.2.4
提高
sparksql
运行的
task
数量
3. RDD
的重用和持久化
3.1
实际开发遇到的情况说明
3.2
如何对
rdd
进行持久化
3.3 rdd
持久化的时可以采用序列化
通过设置参数
spark.sql.shuffle.partitions=500
默认为
200
;
可以适当增大,来提高并行度。 比如设置为
spark.sql.shuffle.partitions=500
如上图所示的计算逻辑:
(
1
)当第一次使用
rdd2
做相应的算子操作得到
rdd3
的时候,就会从
rdd1
开始计算,先读取
HDFS
上的文件,然后对
rdd1
做对应的算子操作得到
rdd2,
再由
rdd2
计算之后得到
rdd3
。同样为了计算得到
rdd4
,前面的逻辑会被重新计算。
(
3
)默认情况下多次对一个
rdd
执行算子操作,去获取不同的
rdd
,都会对这个
rdd
及之前的父
rdd
全部重新计算一次。
这种情况在实际开发代码的时候会经常遇到,但是我们一定要避免一个
rdd
重复计算多次,否则会导致性能急剧降低。
总结:可以把多次使用到的
rdd
,也就是公共
rdd
进行持久化,避免后续需要,再次重新计算,提升效率。
可以调用
rdd
的
cache
或者
persist
方法。
(
1
)
cache
方法默认是把数据持久化到内存中 ,例如:
rdd.cache
,其本质还是调用了
persist
方法
(
2
)
persist
方法中有丰富的缓存级别,这些缓存级别都定义在
StorageLevel
这个
object
中,可以结合实际的应用场
景合理的设置缓存级别。例如:
rdd.persist(StorageLevel.MEMORY_ONLY),
这是
cache
方法的实现。
3.4
广播变量的使用
3.4.1
场景描述
在实际工作中可能会遇到这样的情况,由于要处理的数据量非常大,这个时候可能会在一个
stage
中出现大量的
task
,比如有
1000
个
task
,这些
task
都需要一份相同的数据来处理业务,这份数据的大小为
100M
,该数据会拷贝
1000
份副本,通过网络传输到各个
task
中去,给
task
使用。这里会涉及大量的网络传输开销,同时至少需要的内存
为
1000*100M=100G
,这个内存开销是非常大的。不必要的内存的消耗和占用,就导致了,你在进行
RDD
持久化
到内存,也许就没法完全在内存中放下;就只能写入磁盘,最后导致后续的操作在磁盘
IO
上消耗性能;这对于
spark
任务处理来说就是一场灾难。
由于内存开销比较大,
task
在创建对象的时候,可能会出现堆内存放不下所有对象,就会导致频繁的垃圾回收器的
回收
GC
。
GC
的时候一定是会导致工作线程停止,也就是导致
Spark
暂停工作那么一点时间。频繁
GC
的话,对
Spark
作业的运行的速度会有相当可观的影响。
3.4.2
广播变量引入
Spark
中分布式执行的代码需要传递到各个
executor
的
task
上运行。对于一些只读、固定的数据
,
每次都需要
Driver
广播到各个
Task
上,这样效率低下。广播变量允许将变量只广播(提前广播)给各个
executor
。该
executor
上的各
个
task
再从所在节点的
BlockManager(
负责管理某个
executor
对应的内存和磁盘上的数据
)
获取变量,而不是从
Driver
获取变量,从而提升了效率。
(
1
)如果正常将数据持久化在内存中,那么可能会导致内存的占用过大,这样的话,也许会导致
OOM
内存溢出。
(
2
)当纯内存无法支撑公共
RDD
数据完全存放的时候,就优先考虑使用序列化的方式在纯内存中存储。将
RDD
的每个
partition
的数据,序列化成一个字节数组;序列化后,大大减少内存的空间占用。
(
3
)序列化的方式,唯一的缺点就是,在获取数据的时候,需要反序列化。但是可以减少占用的空间和便于网络传输
(
4
)如果序列化纯内存方式,还是导致
OOM
,内存溢出;就只能考虑磁盘的方式,内存
+
磁盘的普通方式(无序列化)。
(
5
)为了数据的高可靠性,而且内存充足,可以使用双副本机制,进行持久化
持久化的双副本机制,持久化后的一个副本,因为机器宕机了,副本丢了,就还是得重新计算一次;
持久化的每个数据单元,存储一份副本,放在其他节点上面,从而进行容错;
一个副本丢了,不用重新计算,还可以使用另外一份副本。这种方式,仅仅针对你的内存资源极度充足。
比如
: StorageLevel.MEMORY_ONLY_2
3.4.3
使用广播变量后的性能分析
3.4.4
如何使用广播变量
广播变量,初始的时候,就在
Drvier
上有一份副本。通过在
Driver
把共享数据转换成广播变量。
task
在运行的时候,想要使用广播变量中的数据,此时首先会在自己本地的
Executor
对应的
BlockManager
中,尝
试获取变量副本;如果本地没有,那么就从
Driver
远程拉取广播变量副本,并保存在本地的
BlockManager
中;
此后这个
executor
上的
task
,都会直接使用本地的
BlockManager
中的副本。那么这个时候所有该
executor
中的
task
都会使用这个广播变量的副本。也就是说一个
executor
只需要在第一个
task
启动时,获得一份广播变量数据,之后
的
task
都从本节点的
BlockManager
中获取相关数据。
executor
的
BlockManager
除了从
driver
上拉取,也可能从其他节点的
BlockManager
上拉取变量副本,网络距离
越近越好。
比如一个任务需要
50
个
executor
,
1000
个
task
,共享数据为
100M
。
(1)
在不使用广播变量的情况下,
1000
个
task
,就需要该共享数据的
1000
个副本,也就是说有
1000
份数需要大量的网络
传输和内存开销存储。耗费的内存大小
1000*100=100G.
(2)
使用了广播变量后,
50
个
executor
就只需要
50
个副本数据,而且不一定都是从
Driver
传输到每个节点,还可能是就
近从最近的节点的
executor
的
blockmanager
上拉取广播变量副本,网络传输速度大大增加;内存开销
50*100M=5G
总结:
不使用广播变量的内存开销为
100G
,使用后的内存开销
5G
,这里就相差了
20
倍左右的网络传输性能损耗和内存开
销,使用广播变量后对于性能的提升和影响,还是很可观的。
广播变量的使用不一定会对性能产生决定性的作用。比如运行
30
分钟的
spark
作业,可能做了广播变量以后,速度
快了
2
分钟,或者
5
分钟。但是一点一滴的调优,积少成多。最后还是会有效果的。
4.
使用
Kryo
序列化
4.1 spark
序列化介绍
4.2 Kryo
序列化启用后生效的地方