Spark 性能优化:代码中常用的几个调整点

前面三篇文章的三种优化方式调整到位之后会让整个 Spark 作业执行速度有非常明显的提升。

除此之外我们还有很多其它性能优化的手段,但在和前面三种方式比较,正常情况下提升没有那么大。

1,使用广播变量

在 task 执行算子函数运算的时候,如果要用到外部变量,这种时候需要使用广播变量。因为,如果不使用广播变量,那么所使用的外部变量会在每个 task 里会获得一份变量的副本,后续传输到各个 worker 节点去计算的时候一方面会加大很多的网络传输,另一方面在 task 计算的时候会占用太大的内存空间(甚至会用到磁盘空间),读写消耗的 IO 也会很多。

使用广播变量后,初始的时候,只会在 Driver 上有一份副本。当 task 计算的时候,需要使用到广播变量中的数据时,首先会在本地的 executor 对应的 blockManager 中尝试获取,如果这里没有,就会从 driver 远程拉取变量副本(也可能会从距离最近的其它节点上的 blockManager 上拉取数据),并保持在本地的 blockManager 中,此后这个 executor 上的所有 task 会直接使用这份数据。

2,使用 kryo 序列化

Spark 内部默认使用的序列化方式是 Java 的序列化方式。和 kryo 的序列化相比,Java 的序列化方式,速度慢,序列化的数据占用空间相对还是很大,所以使用 kryo 序列化在一定程度上提升性能。

使用 kryo 序列化能优化到的几个地方:算子函数中用到的外部变量,持久化 RDD 时候使用 StorageLevel.MEMORY_ONLY_SER 这个级别时,shuffle 过程。

使用方法:

SparkConf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer").registerKryoClasses(xxxx)

3,使用 fastutil 工具类

fastutil 是扩展了 Java 标准集合框架的类库。他能够提供更小的内存占用和存取速度。在 Spark 内使用到集合存储数据的地方可以尽量使用 fastutil 提供的一些集合。

4,调整数据本地化等待时长

Spark 在 Driver 上分配每个 stage 的 task 去 worker 节点之前,都会计算出每个 task 要计算的是哪个 partition 数据。这里 Spark 会有自己的一套 task 分配算法,优先希望的是 task 刚好分配到其所有处理数据的那个节点,可以省去数据网络传输的性能消耗。

但,如果那个节点上的计算能力或者其他资源已经满了,这时候,Spark 会等待一段时间,如果说在这段时间内这个几点计算能力或者资源释放了,能够满足这个 task 的资源需求的话,就会继续分配到这个节点,如果这段时间内还是老样子,那么久得分配到其它节点上了。

这个等待时间是通过 spark.locality.wait 这个配置参数设置。

5,JVM 调优

a,降低 cache 操作的内存占比: Spark 集群的每个 worker 节点上 executor 都是运行在各自的 JVM 中。这个 JVM 的内存会被划分成两块,一块是用来给 RDD 的 cache、persist 的数据做缓存的,另一块是存储各个算子中函数运算中产生的对象的。这个比例默认是 0.6,也就是只有 40% 的内存空间是给各个算子计算用的,如果计算中产生的数据量过大会频繁触发 minor gc,甚至会触发大量的 full gc,很吃性能。所以在 RDD 缓存使用很少的情况下可以调节下这个参数。

调节参数为:spark.storage.memoryFraction

b,调整 JVM 进程中除了 堆内存以外的内存空间: 有时候,如果你的spark作业处理的数据量特别特别大,几亿数据量。然后spark作业一运行,时不时的报错,shuffle file cannot find,executor、task lost,out of memory。这时候就要考虑设置这个参数了。

调节参数:--conf spark.yarn.executor.memoryOverhead 这个是配置在 shell 脚本里的。

c,调整连接等待时长: 碰到一种情况,没有任何规律:某某file。一串file id。uuid(dsfsfd-2342vs—sdf–sdfsd)。not found。file lost。这种情况很有可能就是某个 worker 上的 executor 拉取另外节点上的数据由于长时间没拉到,超过了超时等待时间,爆出的错误。

调节参数:--conf spark.core.connection.ack.wait.timeout 配置在 shell 脚本里的。

关于堆外内存和 JVM 内存分配的东西可以参考这两偏文章:

https://www.ibm.com/developerworks/cn/analytics/library/ba-cn-apache-spark-memory-management/index.html

https://blog.csdn.net/baolibin528/article/details/54406540

6,shuffle 调优

a,合并 map 端输出文件: shuffle 阶段,前一个 stage 会为后一个 stage 每个 task 准备一份 map 端的输出文件,供后一个 stage 拉取。如果后一个 stage 的 task 数量很多,那么前一个 stage 产生的 map 输出文件特别多,这时候需要开启 map 端文件合并,能够减少很多文件,有助于 shuffle 阶段的性能提升。

调节参数:new SparkConf().set("spark.shuffle.consolidateFiles", "true")

b,调整 map 端内存缓冲和 reduce 端的内存占比: map 端缓冲内存大小默认是 32k,reduce 端用来缓存的内存占比默认是 0.2。

调节参数为:spark.shuffle.file.buffer spark.shuffle.memoryFraction

c,HashShuffleManager 和 SortShuffleManager: 参考:https://my.oschina.net/hblt147/blog/1569936

7,算子调优

MapPartitions提升Map类操作性能、filter过后使用coalesce减少分区数量、foreachPartition优化写数据库性能、repartition解决Spark SQL低并行度的性能问、reduceByKey 在 shuffle 操作时会在 map 端进行一次本地 combine,性能比 groupByKey 要好很多,所以能用 reduceByKey 的地方尽量用 reduceByKey。

发布了82 篇原创文章 · 获赞 148 · 访问量 12万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览