因为Spark是内存当中的计算框架,集群中的任何资源都会让它处于瓶颈,CPU、内存、网络带宽。通常,内存足够的情况之下,网络带宽是瓶颈,这时我们就需要进行一些调优,比如用一种序列化的方式来存储RDD来减少内存使用,这边文章就讲两种方式,数据序列化和内存调优,接下来我们会分几个主题来谈论这个调优问题。

1、数据序列化

(1) Spark默认是使用Java的 ObjectOutputStream框架,它支持所有的继承于java.io.Serializable序列化,如果想要进行调优的话,可以通过继承java.io.Externalizable。这种格式比较大,而且速度慢。

(2)Spark还支持这种方式Kryo serialization,它的速度快,而且压缩比高于 Java的序列化,但是它不支持所有的 Serializable格式,并且需要在程序里面注册。它需要在实例化 SparkContext之前进行注册, 下面是它的使用例子:

import com.esotericsoftware.kryo.Kryo
import org.apache.spark.serializer.KryoRegistrator class MyRegistrator extends KryoRegistrator {
override def registerClasses(kryo: Kryo) {
kryo.register(classOf[MyClass1])
kryo.register(classOf[MyClass2])
}
} // Make sure to set these properties *before* creating a SparkContext!
System.setProperty("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
System.setProperty("spark.kryo.registrator", "mypackage.MyRegistrator")
val sc = new SparkContext(...)

如果对象很大,需要设置这个参数 spark.kryoserializer.buffer.mb,默认是2。

想了解更多关于这个格式的,可以查看这个网址https://github.com/EsotericSoftware/kryo

2、内存调化

这里面需要考虑3点,对象使用的内存、访问这些对象的开销、垃圾回收器的管理开销。

通常,对象访问的速度都很快,但是需要2-5x的空间来存储,因为下面的原因:

1)每一个独立的Java对象,都有一个16字节的“ object header”和关于这个对象的信息,比如指针。

2)Java String类型有40字节的“object header”,然后因为 Unicode,每个字符要存储2个字节,这样10个字符要消耗掉大概60个字节。

3)普通的容器类,比如HashMap和LinkedList,它们采用的是链式的数据结构,它需要封装每个实体,不仅需要头信息,还要有个指针指向下一个实体。

4)原始容器类型通常存储它们为装箱类型,比如java.lang.Integer。

下面我们就来讨论如何确定这些对象的内存开销并且如何进行调优,比如改变数据结构或者序列化存储数据。下面我们讲谈论如何调优Spark的Cache大小以及Java的垃圾回收器。

(1)确定内存使用情况

首先我们要确定内存使用情况,确定数据集的内存使用情况,最好的方法就是创建一个RDD,然后缓存它,然后查看日志,日志会记录出来它的每个分片使用的大小,然后我们可以找个这些分片的大小计算出总大小,如下:

INFO BlockManagerMasterActor: Added rdd_0_1 in memory on mbk.local:50311 (size: 717.5 KB, free: 332.3 MB)
(2)数据结构调优

1) 优先使用数组和原生类型来替代容器类,或者使用fastutil找个包提供的容器类型,fastutil的官方链接是http://fastutil.di.unimi.it/。

2)避免大量的小对象的嵌套结构。

3)使用数字的ID来表示,而不是使用字符串的ID。

4)如果内存小于32GB,设置JVM参数 -XX:+UseCompressedOops为4个字节而不是8个字节;在Java7或者之后的,尝试使用 -XX:+UseCompressedStrings存储ASCII字符串8个比特一个字符 。这些参数可以添加到spark-env.sh,根据我的观察,应该是设置到 SPARK_JAVA_OPTS这个参数上。

(3)序列化RDD存储

强烈建议使用Kryo进行序列化,这也是降低内存使用最简单的方式。

(4)垃圾回收器调优

当我们只使用一次RDD的时候,不会存在这方面的问题。当java需要清除旧的对象给新的对象腾出空间的时候,它需要遍历所有对象,然后找出那些没有使用的。这里最中要的一点是记住,垃圾回收器的代价是和它里面的对象的数量相关的。查看GC是不是一个问题,第一件事就是使用序列化的缓存方式。

GC还可以出现的问题就是执行任务所需要的内存大小,下面我们讲讨论如何控制分配给RDD缓存的空间大小来减轻这个问题。

1)确定GC的影响

添加这些参数到 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps到SPARK_JAVA_OPTS这个参数,让它出书GC的信息,然后运行任务。

2)缓存大小调优

影响GC的一个重要配置参数是分配给缓存RDD的内存大小,Spark默认是使用 66%的可配置内存大小(通过 spark.executor.memory or SPARK_MEM来配置 )来存储RDD,也即是说,只有33%是给任务执行过程当中执行过程当中创建的对象的。

当你的程序慢下来,你发现GC很频繁或内存不够等现象,降低它的值会起到一些效果,我们可以通过这个参数 System.setProperty("spark.storage.memoryFraction", "0.5")来达到这个效果。

3)高级内存调优

java的堆内存是分为两个区间,Young和Old,Young是用来存储短生命周期的对象,Old是用来存储长生命周期的对象。 Young又可以进一步细分为 [Eden, Survivor1, Survivor2]。 一个简单的垃圾过程可以描述为:当 Eden满的时候,一个简单的GC会运行在Eden和依赖它的对象, Survivor1被复制到Survivor2。 Survivor区域进行了交换。如果一个对象足够老或者Survivor2满了,它就会被移到Old区。当Old区也满的时候,一个完整的GC就会触发。

Spark里面的GC调优目标是确保RDD存储在Old区间,并且Young区有足够的空间去存储那些短生命周期的对象。这样可以减少完全的GC去回收那些任务执行中的临时对象。 下面的的这些步骤可能是有用的:

1)检查 GC的统计信息,查看在任务执行完成之前是不是执行过多次的GC,这意味着内存不足以执行任务。

2)当Old区快满的时候,我们可以通过调整这个参数 spark.storage.memoryFraction来减少缓存使用的内存量,少缓存一点对象比拖慢作业执行更好一些。

3)当发生了很多次小的GC,而不是重要的GC时候,我们可以考虑多分配点内存给 Eden,假设一个任务需要使用E大小的内存,我们可以分配给Eden的内存大小为: -Xmn=4/3*E,这个大小同样适用于 survivor区间 。

4)当从HDFS上读取数据的时候,任务的所需内存可以估计为block的大小,一个反压缩的块是2-3倍的大小,我们考虑用3-4个任务来执行,这样我们可以考虑设置Eden的大小为4*3*64MB。

3、其它的考虑

(1)并行的水平

建议是1个CPU核心2-3个任务,可以通过程序的函数的时候传入 numPartitions 参数,或者通过系统变量 spark.default.parallelism来改变。

(2)Reduce任务的内存使用情况

有时候出现 OutOfMemoryError并不是因为RDD太大内存装不下,而是因为执行Reduce任务执行的 groupByKey的结果太大。Spark的 shuffle操作( sortByKey , groupByKey , reduceByKey , join , etc)它会为每一个任务建立一个hash表来执行grouping操作,简单的处理方式就是增加并行水平,这样每个任务的输入集变小。Spark能够支持每个任务200ms的速度,因为它在所有任务共享了JVMs,减小了发布任务的开销,所有可以安全的增加并行水平超过核心数。

(3)使用broadcast存储大的变量

使用Spark里面的broadcast的变量来存储大的变量可以大大减少每个序列化任务的大小和集群发布任务的开销。大对象的任务都可以考虑使用broadcast变量,Spark在master上会打印每个序列化任务的大小,当大小超过20KB的时候,可以考虑调优。

4、总结

这里简短的指出了我们调优的时候需要注意的一些重要的点,通常我们把序列化方式调整为 Kryo并且缓存方式改为序列化存储方式就可以解决大部分的问题了。

Spark调优的更多相关文章

  1. 【Spark学习】Apache Spark调优

    Spark版本:1.1.0 本文系以开源中国社区的译文为基础,结合官方文档翻译修订而来,转载请注明以下链接: http://www.cnblogs.com/zhangningbo/p/4117981. ...

  2. 【Spark调优】提交job资源参数调优

    [场景] Spark提交作业job的时候要指定该job可以使用的CPU.内存等资源参数,生产环境中,任务资源分配不足会导致该job执行中断.失败等问题,所以对Spark的job资源参数分配调优非常重要 ...

  3. 【Spark调优】大表join大表,少数key导致数据倾斜解决方案

    [使用场景] 两个RDD进行join的时候,如果数据量都比较大,那么此时可以sample看下两个RDD中的key分布情况.如果出现数据倾斜,是因为其中某一个RDD中的少数几个key的数据量过大,而另一 ...

  4. 【Spark调优】小表join大表数据倾斜解决方案

    [使用场景] 对RDD使用join类操作,或者是在Spark SQL中使用join语句时,而且join操作中的一个RDD或表的数据量比较小(例如几百MB或者1~2GB),比较适用此方案. [解决方案] ...

  5. 【Spark调优】数据倾斜及排查

    [数据倾斜及调优概述] 大数据分布式计算中一个常见的棘手问题——数据倾斜: 在进行shuffle的时候,必须将各个节点上相同的key拉取到某个节点上的一个task来进行处理,比如按照key进行聚合或j ...

  6. 【Spark调优】Broadcast广播变量

    [业务场景] 在Spark的统计开发过程中,肯定会遇到类似小维表join大业务表的场景,或者需要在算子函数中使用外部变量的场景(尤其是大变量,比如100M以上的大集合),那么此时应该使用Spark的广 ...

  7. 【Spark调优】Kryo序列化

    [Java序列化与反序列化] Java序列化是指把Java对象转换为字节序列的过程:而Java反序列化是指把字节序列恢复为Java对象的过程.序列化使用场景:1.数据的持久化,通过序列化可以把数据永久 ...

  8. 【翻译】Spark 调优 (Tuning Spark) 中文版

    由于Spark自己的调优guidance已经覆盖了很多很有价值的点,因此这里直接翻译一份过来.也作为一个积累. Spark 调优 (Tuning Spark) 由于大多数Spark计算任务是在内存中运 ...

  9. 【Spark调优】Shuffle原理理解与参数调优

    [生产实践经验] 生产实践中的切身体会是:影响Spark性能的大BOSS就是shuffle,抓住并解决shuffle这个主要原因,事半功倍. [Shuffle原理学习笔记] 1.未经优化的HashSh ...

  10. 【Spark调优】:结合业务场景,优选高性能算子

    聚合操作使用reduceByKey/aggregateByKey替代groupByKey 参见我的这篇博客说明 [Spark调优]:如果实在要shuffle,使用map侧预聚合的算子 内存充足前提下使 ...

随机推荐

  1. 二十九、EFW框架开发的系统支持SaaS模式和实现思路

    回<[开源]EFW框架系列文章索引>        EFW框架源代码下载V1.3:http://pan.baidu.com/s/1c0dADO0 EFW框架实例源代码下载:http://p ...

  2. java攻城狮之路(Android篇)--SQLite

    一.Junit    1.怎么使用        在AndroidManifest.xml文件中进行配置, 在manifest借点下配置instrumentation, 在application借点下 ...

  3. 未在本地计算机上注册 Microsoft.Jet.OLEDB.4.0 提供程序

    在C#的web程序中读取服务器端的Excel文件时所报的异常 问题描述:在Visual Studio中运行.调试均没有问题,但是部署到Windows Server 2008  64位操作系统的IIS7 ...

  4. SuperSocket 1.6.4 通过FixedHeaderReceiveFilter解析自定义协议

    SuperSocket 提供了一些通用的协议解析工具, 你可以用他们简单而且快速的实现你自己的通信协议: TerminatorReceiveFilter (SuperSocket.SocketBase ...

  5. js-二维数组和多维数组

    一.二维数组的表示 myarray[][] 二.二维数组的定义 方法一: var a = new Array(); for(var i=0;i<3;i++){ //一维长度为3 a[i] = n ...

  6. GPUImage滤镜之锐化

    应用锐化工具可以快速聚焦模糊边缘,提高图像中某一部位的清晰度或者焦距程度,使图像特定区域的色彩更加鲜明. 在应用锐化工具时,若勾选器选项栏中的“对所有图层取样”复选框,则可对所有可见图层中的图像进行锐 ...

  7. 暴力 + 贪心 --- Codeforces 558C : Amr and Chemistry

    C. Amr and Chemistry Problem's Link: http://codeforces.com/problemset/problem/558/C Mean: 给出n个数,让你通过 ...

  8. P6 EPPM Installation and Configuration Guide 16 R1 April 2016

    P6 EPPM Installation and Configuration Guide 16 R1         April 2016 Contents About Installing and ...

  9. Mysql有没有语法可以在增加列前进行判断该列是否存在

    Mysql没有直接的语法可以在增加列前进行判断该列是否存在,需要写一个存储过程完成同样任务,下面例子是:在sales_order表中增加一列has_sent列 drop procedure if ex ...

  10. 关于DOM树的常见增删操作

    //具体关于DOM的内容可参考我的另外一篇文章"文本对象模型(Document Object Model)". 案例要点:     1.创建并增加元素节点     2.判断是否存在 ...