因为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. 【转载】linux tail命令的使用方法详解

    本文介绍Linux下tail命令的使用方法.linux tail命令用途是依照要求将指定的文件的最后部分输出到标准设备,通常是终端,通俗讲来,就是把某个档案文件的最后几行显示到终端上,假设该档案有更新 ...

  2. 解决Unreal Engine 4.7.6的DerivedDataCache在C盘疯狂膨胀的问题

    打开 YourEngineFolder\Engine\Config\BaseEngine.ini 将 Local=(Type=FileSystem, ReadOnly=, FoldersToClean ...

  3. CentOS6部署VNC服务端

    VNC (Virtual Network Computer)是虚拟网络计算机的缩写.VNC 是在基于 UNIX 和 Linux 操作系统的免费的开源软件,远程控制能力强大,高效实用,其性能可以和 Wi ...

  4. spring boot 拦截器

    @SpringBootApplicationpublic class Application extends WebMvcConfigurerAdapter { public static void ...

  5. iOS-项目搭建

    一.目的:一个小的项目当然不需要这么费劲的搞,到时一个大的项目要是不好好设计一下的话,写到后面就不知道怎么分类或者命名了,搞的项目很乱.为了更好的对本项目的查看,修改和后期的维护.一个好的项目的搭建不 ...

  6. LeetCode——Gas Station

    There are N gas stations along a circular route, where the amount of gas at station i is gas[i]. You ...

  7. sql server 随机读取数据

    --sql server 随机读取数据 * FROM [tablename] ORDER BY NEWID() pk from [tablename] ORDER BY NEWID()) --这两个方 ...

  8. C#语法糖之Cookies操作类 asp.net

    用法: //声名一个数据集合 var listString = new List<string>() { "a", "b", "c&quo ...

  9. java:[1,1] 需要class, interface或enum

    状态: cmd编译.java文件时报异常:java:[1,1] 需要class, interface或enum异常原因: 主要原因是java文件的编码问题. 在中文操作系统中,使用一贯的"j ...

  10. C#设计模式——工厂方法模式(Factory Method Pattern)

    一.概述在软件系统中,经常面临着“某个对象”的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口.如何应对这种变化?如何提供一种封装机制来隔离出“这个易变对象 ...