Spark官方调优文档翻译(转载)

Spark调优

由于大部分Spark计算都是在内存中完成的,所以Spark程序的瓶颈可能由集群中任意一种资源导致,如:CPU、网络带宽、或者内存等。最常见的情况是,数据能装进内存,而瓶颈是网络带宽;当然,有时候我们也需要做一些优化调整来减少内存占用,例如将RDD以序列化格式保存(storing RDDs in serialized form)。本文将主要涵盖两个主题:1.数据序列化(这对于优化网络性能极为重要);2.减少内存占用以及内存调优。同时,我们也会提及其他几个比较小的主题。

数据序列化

序列化在任何一种分布式应用性能优化时都扮演几位重要的角色。如果序列化格式序列化过程缓慢,或者需要占用字节很多,都会大大拖慢整体的计算效率。通常,序列化都是Spark应用优化时首先需要关注的地方。Spark着眼于要达到便利性(允许你在计算过程中使用任何Java类型)和性能的一个平衡。Spark主要提供了两个序列化库:

  • Java serialization: 默认情况,Spark使用Java自带的ObjectOutputStream 框架来序列化对象,这样任何实现了 java.io.Serializable 接口的对象,都能被序列化。同时,你还可以通过扩展 java.io.Externalizable 来控制序列化性能。Java序列化很灵活但性能较差,同时序列化后占用的字节数也较多。
  • Kryo serialization: Spark还可以使用Kryo 库(版本2)提供更高效的序列化格式。Kryo的序列化速度和字节占用都比Java序列化好很多(通常是10倍左右),但Kryo不支持所有实现了Serializable 接口的类型,它需要你在程序中 register 需要序列化的类型,以得到最佳性能。

要切换到使用 Kryo,你可以在 SparkConf 初始化的时候调用 conf.set(“spark.serializer”, “org.apache.spark.serializer.KryoSerializer”)。这个设置不仅控制各个worker节点之间的混洗数据序列化格式,同时还控制RDD存到磁盘上的序列化格式。目前,Kryo不是默认的序列化格式,因为它需要你在使用前注册需要序列化的类型,不过我们还是建议在对网络敏感的应用场景下使用Kryo。

Spark对一些常用的Scala核心类型(包括在Twitter chill 库的AllScalaRegistrar中)自动使用Kryo序列化格式。

如果你的自定义类型需要使用Kryo序列化,可以用 registerKryoClasses 方法先注册:

val conf = new SparkConf().setMaster(...).setAppName(...)
conf.registerKryoClasses(Array(classOf[MyClass1], classOf[MyClass2]))
val sc = new SparkContext(conf)

Kryo的文档(Kryo documentation )中有详细描述了更多的高级选项,如:自定义序列化代码等。

如果你的对象很大,你可能需要增大 spark.kryoserializer.buffer 配置项(config)。其值至少需要大于最大对象的序列化长度。

最后,如果你不注册需要序列化的自定义类型,Kryo也能工作,不过每一个对象实例的序列化结果都会包含一份完整的类名,这有点浪费空间。

内存调优

内存占用调优主要需要考虑3点:1.数据占用的总内存(你多半会希望整个数据集都能装进内存吧);2.访问数据集中每个对象的开销;3.垃圾回收的开销(如果你的数据集中对象周转速度很快的话)。

一般,Java对象的访问时很快的,但同时Java对象会比原始数据(仅包含各个字段值)占用的空间多2~5倍。主要原因有:

  • 每个Java对象都有一个对象头(object header),对象头大约占用16字节,其中包含像其对应class的指针这样的信息。对于一些包含较少数据的对象(比如只包含一个Int字段),这个对象头可能比对象数据本身还大。
  • Java字符串(String)有大约40子节点额外开销(Java String以Char数据的形式保存原始数据,所以需要一些额外的字段,如数组长度等),并且每个字符都以两字节的UTF-16编码在内部保存。因此,10个字符的String很容易就占了60字节。
  • 一些常见的集合类,如 HashMap、LinkedList,使用的是链表类数据结构,因此它们对每项数据都有一个包装器。这些包装器对象不仅其自身就有“对象头”,同时还有指向下一个包装器对象的链表指针(通常为8字节)。
  • 原始类型的集合通常也是以“装箱”的形式包装成对象(如:java.lang.Integer)。

本节只是Spark内存管理的一个概要,下面我们会更详细地讨论各种Spark内存调优的具体策略。特别地,我们会讨论如何评估数据的内存使用量,以及如何改进 – 要么改变你的数据结构,要么以某种序列化格式存储数据。最后,我们还会讨论如何调整Spark的缓存大小,以及如何调优Java的垃圾回收器。

内存管理概览

Spark中内存主要用于两类目的:执行计算和数据存储。执行计算的内存主要用于混洗(Shuffle)、关联(join)、排序(sort)以及聚合(aggregation),而数据存储的内存主要用于缓存和集群内部数据传播。Spark中执行计算和数据存储都是共享同一个内存区域(M)。如果执行计算没有占用内存,那么数据存储可以申请占用所有可用的内存,反之亦然。执行计算可能会抢占数据存储使用的内存,并将存储于内存的数据逐出内存,直到数据存储占用的内存比例降低到一个指定的比例(R)。换句话说,R是M基础上的一个子区域,这个区域的内存数据永远不会被逐出内存。然而,数据存储不会抢占执行计算的内存(否则实现太复杂了)。

这样设计主要有这么几个需要考虑的点。首先,不需要缓存数据的应用可以把整个空间用来执行计算,从而避免频繁地把数据吐到磁盘上。其次,需要缓存数据的应用能够有一个数据存储比例(R)的最低保证,也避免这部分缓存数据被全部逐出内存。最后,这个实现方式能够在默认情况下,为大多数使用场景提供合理的性能,而不需要专家级用户来设置内存使用如何划分。

虽然有两个内存划分相关的配置参数,但一般来说,用户不需要设置,因为默认值已经能够适用于绝大部分的使用场景:

  • spark.memory.fraction 表示上面M的大小,其值为相对于JVM堆内存的比例(默认0.75)。剩余的25%是为其他用户数据结构、Spark内部元数据以及避免OOM错误的安全预留空间(大量稀疏数据和异常大的数据记录)。
  • spark.memory.storageFraction 表示上面R的大小,其值为相对于M的一个比例(默认0.5)。R是M中专门用于缓存数据块,且这部分数据块永远不会因执行计算任务而逐出内存。

评估内存消耗

确定一个数据集占用内存总量最好的办法就是,创建一个RDD,并缓存到内存中,然后再到web UI上”Storage”页面查看。页面上会展示这个RDD总共占用了多少内存。

要评估一个特定对象的内存占用量,可以用 SizeEstimator.estimate 方法。这个方法对试验哪种数据结构能够裁剪内存占用量比较有用,同时,也可以帮助用户了解广播变量在每个执行器堆上占用的内存量。

数据结构调优

减少内存消耗的首要方法就是避免过多的Java封装(减少对象头和额外辅助字段),比如基于指针的数据结构和包装对象等。以下有几条建议:

  1. 设计数据结构的时候,优先使用对象数组和原生类型,减少对复杂集合类型(如:HashMap)的使用。fastutil 提供了一些很方便的原声类型集合,同时兼容Java标准库。
  2. 尽可能避免嵌套大量的小对象和指针。
  3. 对应键值应尽量使用数值型或枚举型,而不是字符串型。
  4. 如果内存小于32GB,可以设置JVM标志参数 -XX:+UseCompressdOops 将指针设为4字节而不是8字节。你可以在  spark-env.sh 中设置这个参数。

序列化RDD存储

如果经过上面的调整后,存储的数据对象还是太大,那么你可以试试将这些对象以序列化格式存储,所需要做的只是通过 RDD persistence API 设置好存储级别,如:MEMORY_ONLY_SER。Spark会将RDD的每个分区以一个巨大的字节数组形式存储起来。以序列化格式存储的唯一缺点就是访问数据会变慢一点,因为Spark需要反序列化每个被访问的对象。如果你需要序列化缓存数据,我们强烈建议你使用Kryo(using Kryo),和Java序列化相比,Kryo能大大减少序列化对象占用的空间(当然也比原始Java对象小很多)。

垃圾回收调优

JVM的垃圾回收在某些情况下可能会造成瓶颈,比如,你的RDD存储经常需要“换入换出”(新RDD抢占了老RDD内存,不过如果你的程序没有这种情况的话那JVM垃圾回收一般不是问题,比如,你的RDD只是载入一次,后续只是在这一个RDD上做操作)。当Java需要把老对象逐出内存的时候,JVM需要跟踪所有的Java对象,并找出那些对象已经没有用了。概括起来就是,垃圾回收的开销和对象个数成正比,所以减少对象的个数(比如用 Int数组取代 LinkedList),就能大大减少垃圾回收的开销。当然,一个更好的方法就如前面所说的,以序列化形式存储数据,这时每个RDD分区都只包含有一个对象了(一个巨大的字节数组)。在尝试其他技术方案前,首先可以试试用序列化RDD的方式(serialized caching)评估一下GC是不是一个瓶颈。

如果你的作业中各个任务需要的工作内存和节点上存储的RDD缓存占用的内存产生冲突,那么GC很可能会出现问题。下面我们将讨论一下如何控制好RDD缓存使用的内存空间,以减少这种冲突。

衡量GC的影响

GC调优的第一步是统计一下,垃圾回收启动的频率以及GC所使用的总时间。给JVM设置一下这几个参数(参考Spark配置指南 –  configuration guide,查看Spark作业中的Java选项参数):-verbose:gc -XX:+PrintGCDetails,就可以在后续Spark作业的worker日志中看到每次GC花费的时间。注意,这些日志是在集群worker节点上(在各节点的工作目录下stdout文件中),而不是你的驱动器所在节点。

高级GC调优

为了进一步调优GC,我们就需要对JVM内存管理有一个基本的了解:

  • Java堆内存可分配的空间有两个区域:新生代(Young generation)和老生代(Old generation)。新生代用以保存生存周期短的对象,而老生代则是保存生存周期长的对象。
  • 新生代区域被进一步划分为三个子区域:Eden,Survivor1,Survivor2。
  • 简要描述一下垃圾回收的过程:如果Eden区满了,则启动一轮minor GC回收Eden中的对象,生存下来(没有被回收掉)的Eden中的对象和Survivor1区中的对象一并复制到Survivor2中。两个Survivor区域是互相切换使用的(就是说,下次从Eden和Survivor2中复制到Survivor1中)。如果某个对象的年龄(每次GC所有生存下来的对象长一岁)超过某个阈值,或者Survivor2(下次是Survivor1)区域满了,则将对象移到老生代(Old区)。最终如果老生代也满了,就会启动full GC。

Spark GC调优的目标就是确保老生代(Old generation )只保存长生命周期RDD,而同时新生代(Young generation )的空间又能足够保存短生命周期的对象。这样就能在任务执行期间,避免启动full GC。以下是GC调优的主要步骤:

  • 从GC的统计日志中观察GC是否启动太多。如果某个任务结束前,多次启动了full GC,则意味着用以执行该任务的内存不够。
  • 如果GC统计信息中显示,老生代内存空间已经接近存满,可以通过降低 spark.memory.storageFraction 来减少RDD缓存占用的内存;减少缓存对象总比任务执行缓慢要强!
  • 如果major GC比较少,但minor GC很多的话,可以多分配一些Eden内存。你可以把Eden的大小设为高于各个任务执行所需的工作内存。如果要把Eden大小设为E,则可以这样设置新生代区域大小:-Xmn=4/3*E。(放大4/3倍,主要是为了给Survivor区域保留空间)
  • 举例来说,如果你的任务会从HDFS上读取数据,那么单个任务的内存需求可以用其所读取的HDFS数据块的大小来评估。需要特别注意的是,解压后的HDFS块是解压前的2~3倍大。所以如果我们希望保留3~4个任务并行的工作内存,并且HDFS块大小为64MB,那么可以评估Eden的大小应该设为 4*3*64MB。
  • 最后,再观察一下垃圾回收的启动频率和总耗时有没有什么变化。

我们的很多经验表明,GC调优的效果和你的程序代码以及可用的总内存相关。网上还有不少调优的选项说明(many more tuning options),但总体来说,就是控制好full GC的启动频率,就能有效减少垃圾回收开销。

其他注意事项

并行度

一般来说集群并不会满负荷运转,除非你吧每个操作的并行度都设得足够大。Spark会自动根据对应的输入文件大小来设置“map”类算子的并行度(当然你可以通过一个SparkContext.textFile等函数的可选参数来控制并行度),而对于想 groupByKey 或reduceByKey这类 “reduce” 算子,会使用其各父RDD分区数的最大值。你可以将并行度作为构建RDD第二个参数(参考spark.PairRDDFunctions ),或者设置 spark.default.parallelism 这个默认值。一般来说,评估并行度的时候,我们建议2~3个任务共享一个CPU。

Reduce任务的内存占用

如果RDD比内存要大,有时候你可能收到一个OutOfMemoryError,但其实这是因为你的任务集中的某个任务太大了,如reduce任务groupByKey。Spark的混洗(Shuffle)算子(sortByKey,groupByKey,reduceByKey,join等)会在每个任务中构建一个哈希表,以便在任务中对数据分组,这个哈希表有时会很大。最简单的修复办法就是增大并行度,以减小单个任务的输入集。Spark对于200ms以内的短任务支持非常好,因为Spark可以跨任务复用执行器JVM,任务的启动开销很小,因此把并行度增加到比集群中总CPU核数还多是没有任何问题的。

广播大变量

使用SparkContext中的广播变量相关功能(broadcast functionality)能大大减少每个任务本身序列化的大小,以及集群中启动作业的开销。如果你的Spark任务正在使用驱动器(driver)程序中定义的巨大对象(比如:静态查询表),请考虑使用广播变量替代之。Spark会在master上将各个任务的序列化后大小打印出来,所以你可以检查一下各个任务是否过大;通常来说,大于20KB的任务就值得优化一下。

数据本地性

数据本地性对Spark作业往往会有较大的影响。如果代码和其所操作的数据在统一节点上,那么计算速度肯定会更快一些。但如果二者不在一起,那必然需要挪动其中之一。一般来说,挪动序列化好的代码肯定比挪动一大堆数据要快。Spark就是基于这个一般性原则来构建数据本地性的调度。

数据本地性是指代码和其所处理的数据的距离。基于数据当前的位置,数据本地性可以划分成以下几个层次(按从近到远排序):

  • PROCESS_LOCAL 数据和运行的代码处于同一个JVM进程内。
  • NODE_LOCAL 数据和代码处于同一节点。例如,数据处于HDFS上某个节点,而对应的执行器(executor)也在同一个机器节点上。这会比PROCESS_LOCAL稍微慢一些,因为数据需要跨进程传递。
  • NO_PREF 数据在任何地方处理都一样,没有本地性偏好。
  • RACK_LOCAL 数据和代码处于同一个机架上的不同机器。这时,数据和代码处于不同机器上,需要通过网络传递,但还是在同一个机架上,一般也就通过一个交换机传输即可。
  • ANY 数据在网络中其他未知,即数据和代码不在同一个机架上。

Spark倾向于让所有任务都具有最佳的数据本地性,但这并非总是可行的。某些情况下,可能会出现一些空闲的执行器(executor)没有待处理的数据,那么Spark可能就会牺牲一些数据本地性。有两种可能的选项:a)等待已经有任务的CPU,待其释放后立即在同一台机器上启动一个任务;b)立即在其他节点上启动新任务,并把所需要的数据复制过去。

而通常,Spark会等待一小会,看看是否有CPU会被释放出来。一旦等待超时,则立即在其他节点上启动并将所需的数据复制过去。数据本地性各个级别之间的回落超时可以单独配置,也可以在统一参数内一起设定;详细请参考 configuration page 中的 spark.locality 相关参数。如果你的任务执行时间比较长并且数据本地性很差,你就应该试试调大这几个参数,不过默认值一般都能适用于大多数场景了。

总结

本文是一个简短的Spark调优指南,列举了Spark应用调优一些比较重要的考虑点 – 最重要的就是,数据序列化和内存调优。对于绝大多数应用来说,用Kryo格式序列化数据能够解决大多数的性能问题。如果您有其他关于性能调优最佳实践的问题,欢迎邮件咨询(Spark mailing list )。

 

该文转自 http://ifeve.com/spark-tuning/

官方英文地址 http://spark.apache.org/docs/latest/tuning.html








本文转自shishanyuan博客园博客,原文链接:   http://www.cnblogs.com/shishanyuan/p/8481854.html ,如需转载请自行联系原作者



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/286938.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《DOS命令全集(中英文对照)》CHM版.CHM

http://pan.baidu.com/s/1pLrhAzx转载于:https://www.cnblogs.com/micro-chen/p/5692802.html

判断一个字符串是否为另外一个字符串旋转之后的字符串。

★判断一个字符串是否为另外一个字符串旋转之后的字符串。例如:给定s1 = AABCD和s2 BCDAA,返回1,给定s1abcd和s2ACBD,返回0.AABCD左旋一个字符得到ABCDA AABCD右旋一个字符得到DAABC AABCD左旋两…

Flutter之Padding

1 、Padding介绍 Padding用来为子元素添加填充,也就是指定子元素与容器边界的距离,作用基本上与Android中ViewGroup的padding属性差不多 const Padding({Key key,required this.padding,Widget child,}) : assert(padding ! null),super(key: key, chil…

【无人机组装与调试】第三章 舵机安装与调整

【无人机组装与调试】系列课程全集: 第一章 概述 第二章 关于新西达30A电调说明书的问题 第三章 舵机安装与调整 第四章 F450四轴装机实例-选择机型、需要的器材工具材料 第五章 无人机遥控器 第六章 电调、电池、电机 舵机就是一种有输出轴的小传动装置。这个输出轴能够通过向…

其实python面向对象3分钟就可以入门(14)

本系列文章将会以通俗易懂的对话方式进行教学,对话中将涵盖了新手在学习中的一般问题。此系列将会持续更新,包括别的语言以及实战都将使用对话的方式进行教学,基础编程语言教学适用于零基础小白,之后实战课程也将会逐步更新。 若…

定制ASP.NET 6.0的应用配置

大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进。本文的主题是应用程序配置。要介绍的是如何使用配置、如何自定义配置,以采用不同的方…

mysql optimization

EXPLAIN 命令详解 http://www.cnblogs.com/gomysql/p/3720123.html http://www.cnblogs.com/Aiapple/p/5697229.html http://www.cnblogs.com/xuanzhi201111/p/4175635.html https://dev.mysql.com/doc/refman/5.7/en/optimization.html Mysql 执行计划(Explain&…

云服务器cpu性能,云服务器cpu性能

云服务器cpu性能 内容精选换一换CPU积分是一种用来衡量云服务器计算、存储以及网络配置利用率的方式。云服务器利用CPU积分机制保证云服务器基准性能,解决超分云服务器长期占用CPU资源的问题。使用CPU积分机制的弹性云服务器适用于平时CPU负载不高、但突发时可接受因…

Flutter之Decoration

1、不废话,先爆照看效果 2、Decoration介绍 Flutter的Decoration可以设置:背景色 背景图 边框 圆角 阴影 渐变色 的等属性,有点像android里面的shape,Decoration 是基类,它的子类有下面这些 BoxDecoration:实现边框、…

DRBD 部署

主备模式DRBD1:eth0:10.0.0.3eth1:172.16.1.3 用于心跳线和数据同步(在工作中,一般把心跳线分开)DRBD2:eth0:10.0.0.4eth1:172.16.1.4 用于心跳线和数据同步(在工作中,一…

.net 服务器端自定义分页控件 简单示例

使用效果如图&#xff1a; 先将控件添加到工具箱 将控件拖入到页面 会自动生成如下代码 <pager:pager ID"Pager1" runat"server" Pagesize"2" OnPageIndexChange"Pager1_PageIndexChange1"> </pager:pager> 后台代码自己…

XenApp_XenDesktop_7.6实战篇之十五:StoreFront的配置

1. StoreFront服务器证书申请 我们将为StoreFront 申请Web 服务器证书&#xff0c;将此证书应用到StoreFront 的IIS 站点上&#xff0c;并将IIS 的443 端口绑定此证书&#xff0c;以使我们从内网可以安全访问StoreFront 站点。也为后续的与NetScaler 集成做好准备。 1.1 在开…

【无人机组装与调试】第四章 F450四轴装机实例-选择机型、需要的器材工具材料

【无人机组装与调试】系列课程全集: 第一章 概述 第二章 关于新西达30A电调说明书的问题 第三章 舵机安装与调整 第四章 F450四轴装机实例-选择机型、需要的器材工具材料 第五章 无人机遥控器 第六章 电调、电池、电机 第四章 F450四轴装机实例-选择机型、需要的器材工具材料…

面向对象类的使用(15)

本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖了新手在学习中的一般问题。此系列将会持续更新&#xff0c;包括别的语言以及实战都将使用对话的方式进行教学&#xff0c;基础编程语言教学适用于零基础小白&#xff0c;之后实战课程也将会逐步更新。 若…

服务器安装红帽系统进入不图形界面,CentOS 安装图形化界面方法

登录系统&#xff0c;使用yum 安装#yum groupinstall X Window System -y安装GNOME桌面环境#yum groupinstall GNOME Desktop Environment -y如果安装KDE桌面环境#yum groupinstall KDE (K Desktop Environment) -y如果发生错误&#xff1a;Warning: Group GNOME Desktop Env…

Mybatis批量更新数据

Mybatis批量更新数据 第一种方式 [html] view plaincopy print?<update id"updateBatch" parameterType"Map"> update aa set a#{fptm}, b#{csoftrain} where c in …

MASA Auth - SSO与Identity设计

AAAAAAAA即认证、授权、审计、账号&#xff08;Authentication、Authorization、Audit、Account&#xff09;。在安全领域我们绕不开的两个问题&#xff1a;授权过程可靠&#xff1a;让第三方程序能够访问所需资源又不泄露用户数据&#xff0c;常用的多方授权协议主要有 OAuth2…

Flutter之ConstrainedBox、SizedBox、UnconstrainedBox(尺寸限制类容器)

1 ConstrainedBox、SizedBox、UnconstrainedBox介绍 1)、ConstrainedBox用于对子组件添加额外的约束。例如&#xff0c;如果你想让子组件的最小高度是80像素 ConstrainedBox({Key key,required this.constraints,Widget child,}) 我们可以看到这里有个constraints final BoxC…

网页用数学公式编辑器 可以集成到FCKeditor

网页用数学公式编辑器 可以集成到FCKeditor 公司有个项目是针对学校的&#xff0c;老师希望能在网页里编辑数学公式&#xff0c;找了很多工具都不行&#xff0c;最后只能自己动手了。用了两天的时间弄出了这个东西&#xff0c;希望对大家有用。本程序可以单独使用&#xff0c;…

【无人机组装与调试】 第五章 无人机遥控器

【无人机组装与调试】系列课程全集: 第一章 概述 第二章 关于新西达30A电调说明书的问题 第三章 舵机安装与调整 第四章 F450四轴装机实例-选择机型、需要的器材工具材料 第五章 无人机遥控器 第六章 电调、电池、电机 一、遥控器的概念 遥控器是一种无线发射装置,通过现代的…