(转载)Spark 性能调优

Spark 性能优化概览:
由于Spark 的计算本质是分布式计算。
Spark程序的性能可能因为集群中的任何因素出现瓶颈:CPU,网络带宽、内存。

如果内存比较紧张,不足以放下所有数据,需要对内存的使用进行性能优化。
比如说:使用一些手段来减少内存消耗。

park的性能优化,其实主要就是在对于内存的使用进行调优。

Spark 性能优化的主要手段:

1、使用高性能的序列化类库
2、优化数据结构
3、RDD持久化、checkpoint
4、使用序列化的持久化级别:MEMORY_ONLY不序列化,MEMORY_ONLY_SER序列化
5、JAVA虚拟机垃圾回收调优
6、Shuffle调优。1.x版本很常见。2.x版本,官方解决这个问题了。

一、诊断Spark内存使用
首先要看到内存使用情况,才能进行针对性优化

1.1 内存花费
(1)每个Java对象,都有一个对象头,占16个字节,主要包含对象的元信息,比如说类的指针。
如果对象本身很小,他的对象头可能比实际数据都大。

(2)String对象,会比他内部原始数据,多出40个字节。

String内部使用char数组来保存内部的字符串序列,需要保存一些诸如数组长度的信息。
如果String使用utf-16编码,每个字符会占用2个字节,比如,包含10个字符的String,占用 10 * 2 + 40 = 60个字节

(3)集合类型,例如HashMap,LinkedList,内部使用链表数据结构,会对每个数据使用Entry对象包装。
Entry对象,不光有对象头,还有指向下一个entry的指针,通常占用8个字节。

(4)原始数据类型,会使用包装类型来存储元素。

1.2 如何判断Spark程序消耗内存情况?

把内存信息相加,就是内存占用量 

二、使用高性能序列化类库
2.1 数据序列化概述
数据序列化就是将对象或者数据结构转换成特定的格式,使其可以在网络中传输,或者可以存储在内存或者文件中。
反序列化则是相反的操作,将对象从序列化的数据中还原出来。

数据序列化后的数据格式,可以是二进制,可以是XML,也可以是JSON等任何格式。

对象、数据序列化的重点在于数据的交换和传输。

在任何分布式系统中,序列化都是扮演一个重要的角色。如果使用的序列化技术,操作很慢,或者序列化后数据量还是很大,会让分布式系统的应用程序,性能下降。所以,进行Spark性能优化的第一步,就是进行序列化的性能优化。

Spark自身默认会在一些地方对数据进行序列化,shuffle。另外,如果我们使用了外部数据(例如:自定义类型),我们也要让其可序列化

Spark本身对序列化的便捷性和性能进行了取舍和权衡。Spark默认情况下,倾向于便捷性。使用了Java自身序列化机制,很方便使用。但是,Java序列化机制性能不高,序列化速度比较慢,而且序列化后的数据比较大,比较占用内存空间。

2.2 kryo
spark 支持使用kryo序列化类库来进行序列化
比java序列化机制更快,且占用数据空间更小,比Java序列化数据占用空间小10倍。

2.3 如何使用kryo序列化机制
(1)设置spark conf


(2)使用kryo时,要求需要序列化的类,提前进行注册,以获得高性能

2.4 kryo类库的优化
(1)优化缓存大小
如果注册的序列化的自定义类型,本身特别大,比如包含了超过100个字段。就会导致序列化的对象过大,此时需要对kryo本身进行优化。因为kryo本身内部缓存可能不能存放这么大的class对象。

需要设置:


(2)预先注册自定义类型

推荐预先注册要序列化的自定义类型。

三、优化数据结构
3.1 概述
要减少内存的消耗,除了使用高性能的序列化类库外,还有一个很重要的事情,就是优化数据结构,避免语法特性中所导致的额外内存开销。
核心:优化算子函数内部使用到的局部数据或算子函数外部的数据
目的:减少内存的消耗和占用

3.2 如何做?
(1)优先使用数组以及字符串,而不是集合类。
即:优先使用Array,而不是ArrayList,LinkedList,hashMap

使用 int[] 比 List 节省内存

(2)将对象转换成字符串


(3)避免使用多层嵌套对象结构

  
(4)对于某些能够避免的场景,尽量使用int代替string

原因:int类型占用更少内存

四、RDD持久化、checkpoint
(1)如果程序中,对某一个RDD,基于它进行了多次transformation或者action操作。那么就非常有必要对其进行持久化操作,以避免对一个RDD反复进行计算。

(2)此外,如果要保证在RDD的持久化数据可能丢失的情况下,还要保证高性能,那么可以对RDD进行Checkpoint操作。

五、使用序列化的持久化级别
MEMORY_ONLY不序列化
MEMORY_ONLY_SER序列化

(1)处理对多次使用的 RDD 进行持久化操作之外,还可以进一步优化其性能。因为很可能,RDD 的数据从持久化到内存,或者磁盘中的。那么,此时如果内存大小不是特别充足,完全可以使用序列化的持久化级别,比如 MEMORY_ONLY_SER, MEMORY_AND_DISK_SER等。
使用 RDD.persist(StorageLevel.MEMORY_ONLY_SER)

(2)这样的话,将数据序列化之后,再持久化,可以大大减少对内存的消耗。此时,如果数据量小了之后,如果要写入磁盘,那么磁盘IO 性能消耗也比较小。

(3)对 RDD 持久化序列化之后,RDD 的每个 partition 的数据,都是序列化为一个巨大的字节数组。这样,对于内存消耗就小了,但是唯一缺点就是,获取 RDD 数据时,需要对其进行反序列化,会增大其性能开销。

(4)因此,对于序列化的持久化级别,还可以进一步优化,也就是使用 Kryo 序列化类库,这样可以获得更快的序列化速度,并且占用更少的内存空间。但是,如果RDD 的元素,是自定义类型的话,在 Kryo 中提前注册自定义类型。

六、Java虚拟机 垃圾回收 调优
6.1 背景
如果在计算的时候,引入大量数据,那么java虚拟机的垃圾回收就可能成为一个性能瓶颈。
Java虚拟机会定期进行垃圾回收,此时就会追踪所有Java对象,并且在垃圾回收时,找到那些已经不再使用的对象,清理旧对象,来给新对象腾出内存空间。垃圾回收的性能开销,是跟内存中的对象的数量成正比。
在做虚拟机调优之前,必须先做好上面的调优工作,这样才有意义。

6.2 Spark GC 原理


6.3 检测垃圾回收
我们可以对垃圾回收进行检测,包括多久进行一次垃圾回收,以及每次垃圾回收消耗的事件。 

6.4 优化Executor内存比例
目的:减少GC次数

对于GC调优来说,最重要就是要调节RDD缓存占用的内存空间 与 算子执行时创建对象所占用的内存空间的比例。
默认情况下,spark使用60%的内存空间来缓存RDD,在task运行时创建的对象,只有40%的空间来存放。在这种情况下,很可能因为内存空间不足,task创建对象过大,触发垃圾回收。极端情况下,会频繁触发垃圾回收。

使用方式:


综合使用序列化持久化级别,降低内存消耗。

 G调优:


七、Shuffle
注意:2.0以后,shuffle调优官方已经完成。1.x中,会有此问题

 shuffle调优前:

shuffle调优后:


八、其他调优
广播共享数据(这里不做过多解释)
————————————————
原文链接:(1条消息) Spark 性能调优_故明所以的博客-CSDN博客_spark优化https://blog.csdn.net/weixin_43520450/article/details/106169843?ops_request_misc=&request_id=&biz_id=102&utm_term=spark%E4%BC%98%E5%8C%96&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-8-106169843.pc_search_result_control_group&spm=1018.2226.3001.4187

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值