Spark调优简单总结

来源:互联网 发布:找一份网络兼职 编辑:程序博客网 时间:2024/06/08 08:42

Spark作为内存计算框架,需要做一些优化调整来减少内存占用,例如将RDD以序列化格式保存。总结为两大块:1,数据序列化;2,减少内存占用以及内存调优。

数据序列化

Spark着眼于便利性和性能的一个平衡,Spark主要提供了两个序列化库:

Java Serialization:默认情况,Java序列化很灵活但性能较差,同时序列化后占用的字节数也较多。

Kryo SerializationKryo的序列化速度和字节占用都比Java序列化好通常10倍左右,但Kryo不支持所有实现了Serializable接口的类型,需要在程序中register需要序列化的类型,已得到最佳性能。Spark对一些常用的Scala核心类型自动使用Kryo序列化格式。如果是自定义类型需要使用Kryo序列化,用registerKryoClasses方法先注册。

val conf = new SparkConf.setMaster(…).setAppName(…)

conf.registerKryoClasses(Array(classOf[MyClass],classOf[MyClass2]))

val sc=new SparContext(conf)

如果不进行类型的注册,Kryo也能工作,但是每个对象实例的序列化结果都包含一份完整的类名,浪费空间。

内存调优

考虑3点:1数据占用的总内存2访问数据集中每个对象的开销3垃圾回收的开销

内存管理

Spark中内存主要两类:计算和存储。两者都共享一个内存区域(M),两者都可以借用,存储内存有一个不再外借的家底(R)。spark.memory.fraction表示M,默认JVM堆内存的0.75spark.memory.storageFraction表示R,默认相对M0.5

评估内存消耗

看一个数据集占用多大内存:创建一个RDD,缓存到内存中,在web UIstorage页面查看。

评估一个特定对象的内存占用量,用SizeEstimator.estimate方法,可帮助用户了解广播变量在每个执行器堆上占用的内存量。

数据结构优化

Java对象的访问很快,但是比原始数据占用的空间多2-5倍。

减少内存消耗的首要方法避免过多的Java封装,比如基于指针的数据结构和包装对象等。

优先使用对象数组和原生类型,减少复杂集合类型如HashMap

尽量避免嵌套大量的小对象和指针。

对应键值尽量使用数值类型或枚举类型,而不是字符串。

如果内存小于32GB,设置JVM标志参数-XX:+UseCompressdOops将指针设为4字节而不是8字节,在spark-env.sh中设置。

序列化RDD存储

通过RDD persistence API设置好存储级别。将RDD的每个分区以一个巨大的字节数组形式存储起来。牺牲一点访问数据速度换来空间。

垃圾回收调优

垃圾回收的开销和对象合数成正比,所以减少对象的个数,就能大大减少垃圾回收的开销。序列化存储数据,每个RDD就是一个对象。缓存RDD占用的内存可能跟工作所需的内存打架,需要控制好。

衡量GC的影响

GC调优嘛,无非就是GC回收启动的频率和GC所花时间的平衡。JVM参数:-verbose:gc-XX:+PrintGCDetails,在集群worker节点上看到每次GC所花时间。

高级GC调优

Spark GC调优的目标就是确保老年代只保存长生命周期RDD,而同时新生代的空间足够保存短生命周期的对象,避免Full GC

统计日志观察,Full GC多次,说明内存不够。老年代爆满,调低spark.memory.storageFraction来减少RDD缓存占用的内存。

如果minor GC很多,多分配Eden内存。

并行度

可将并行度作为构建RDD第二个参数,或者设置spark.default.parallelism这个默认值。评估并行度的时候,建议2-3个任务共享一个CPU

Reduce任务的内存占用

RDD比内存大,出现OOM,这是因为任务集中的某个reduce任务太大了, 增大并行度。

广播大变量

Spark任务使用driver中定义的巨大对象,比如表join中的小表,用广播变量广播小表。

数据本地性

PROCESS_LOCAL数据和运行的代码在同一个JVM进程中

NODE_LOCAL数据和代码在同一个节点上。数据跨进程传递。Spark作为内存计算框架,需要做一些优化调整来减少内存占用,例如将RDD以序列化格式保存。总结为两大块:1,数据序列化;2,减少内存占用以及内存调优。
数据序列化
Spark着眼于便利性和性能的一个平衡,Spark主要提供了两个序列化库:
Java Serialization:默认情况,Java序列化很灵活但性能较差,同时序列化后占用的字节数也较多。
Kryo Serialization:Kryo的序列化速度和字节占用都比Java序列化好通常10倍左右,但Kryo不支持所有实现了Serializable接口的类型,需要在程序中register需要序列化的类型,已得到最佳性能。Spark对一些常用的Scala核心类型自动使用Kryo序列化格式。如果是自定义类型需要使用Kryo序列化,用registerKryoClasses方法先注册。
val conf = new SparkConf.setMaster(…).setAppName(…)
conf.registerKryoClasses(Array(classOf[MyClass],classOf[MyClass2]))
val sc=new SparContext(conf)
如果不进行类型的注册,Kryo也能工作,但是每个对象实例的序列化结果都包含一份完整的类名,浪费空间。
内存调优
考虑3点:1,数据占用的总内存;2访问数据集中每个对象的开销;3垃圾回收的开销。
内存管理
Spark中内存主要两类:计算和存储。两者都共享一个内存区域(M),两者都可以借用,存储内存有一个不再外借的家底(R)。spark.memory.fraction表示M,默认JVM堆内存的0.75。spark.memory.storageFraction表示R,默认相对M的0.5。
评估内存消耗
看一个数据集占用多大内存:创建一个RDD,缓存到内存中,在web UI上storage页面查看。
评估一个特定对象的内存占用量,用SizeEstimator.estimate方法,可帮助用户了解广播变量在每个执行器堆上占用的内存量。
数据结构优化
Java对象的访问很快,但是比原始数据占用的空间多2-5倍。
减少内存消耗的首要方法避免过多的Java封装,比如基于指针的数据结构和包装对象等。
优先使用对象数组和原生类型,减少复杂集合类型如HashMap。
尽量避免嵌套大量的小对象和指针。
对应键值尽量使用数值类型或枚举类型,而不是字符串。
如果内存小于32GB,设置JVM标志参数-XX:+UseCompressdOops将指针设为4字节而不是8字节,在spark-env.sh中设置。
序列化RDD存储
通过RDD persistence API设置好存储级别。将RDD的每个分区以一个巨大的字节数组形式存储起来。牺牲一点访问数据速度换来空间。
垃圾回收调优
垃圾回收的开销和对象合数成正比,所以减少对象的个数,就能大大减少垃圾回收的开销。序列化存储数据,每个RDD就是一个对象。缓存RDD占用的内存可能跟工作所需的内存打架,需要控制好。
衡量GC的影响
GC调优嘛,无非就是GC回收启动的频率和GC所花时间的平衡。JVM参数:-verbose:gc-XX:+PrintGCDetails,在集群worker节点上看到每次GC所花时间。
高级GC调优
Spark GC调优的目标就是确保老年代只保存长生命周期RDD,而同时新生代的空间足够保存短生命周期的对象,避免Full GC。
统计日志观察,Full GC多次,说明内存不够。老年代爆满,调低spark.memory.storageFraction来减少RDD缓存占用的内存。
如果minor GC很多,多分配Eden内存。
并行度
可将并行度作为构建RDD第二个参数,或者设置spark.default.parallelism这个默认值。评估并行度的时候,建议2-3个任务共享一个CPU。
Reduce任务的内存占用
RDD比内存大,出现OOM,这是因为任务集中的某个reduce任务太大了, 增大并行度。
广播大变量
Spark任务使用driver中定义的巨大对象,比如表join中的小表,用广播变量广播小表。
数据本地性
PROCESS_LOCAL数据和运行的代码在同一个JVM进程中
NODE_LOCAL数据和代码在同一个节点上。数据跨进程传递。
NO_PREF数据在任何地方处理都一样,没本地性偏好
RACK_LOCAL数据和代码处在同一个机架的不同机器。数据通过交换机传输。
ANY数据和代码不在同一个机器上。

NO_PREF数据在任何地方处理都一样,没本地性偏好

RACK_LOCAL数据和代码处在同一个机架的不同机器。数据通过交换机传输。

ANY数据和代码不在同一个机器上。

原创粉丝点击