随着Spark的逐渐成熟完善, 愈来愈多的可配置参数被添加到Spark中来, 本文试图经过阐述这其中部分参数的工做原理和配置思路, 和你们一块儿探讨一下如何根据实际场合对Spark进行配置优化。算法
因为篇幅较长,因此在这里分篇组织,若是要看最新完整的网页版内容,能够戳这里:http://spark-config.readthedocs.org/,主要是便于更新内容数据结构
Storage相关配置参数性能
spark.local.dir优化
这个看起来很简单,就是Spark用于写中间数据,如RDD Cache,Shuffle,Spill等数据的位置,那么有什么能够注意的呢。spa
首先,最基本的固然是咱们能够配置多个路径(用逗号分隔)到多个磁盘上增长总体IO带宽,这个你们都知道。对象
其次,目前的实现中,Spark是经过对文件名采用hash算法分布到多个路径下的目录中去,若是你的存储设备有快有慢,好比SSD+HDD混合使用,那么你能够经过在SSD上配置更多的目录路径来增大它被Spark使用的比例,从而更好地利用SSD的IO带宽能力。固然这只是一种变通的方法,终极解决方案仍是应该像目前HDFS的实现方向同样,让Spark可以感知具体的存储设备类型,针对性的使用。队列
须要注意的是,在Spark 1.0 之后,SPARK_LOCAL_DIRS(Standalone, Mesos) or LOCAL_DIRS (YARN)参数会覆盖这个配置。好比Spark On YARN的时候,Spark Executor的本地路径依赖于Yarn的配置,而不取决于这个参数。内存
spark.executor.memory资源
Executor 内存的大小,和性能自己固然并无直接的关系,可是几乎全部运行时性能相关的内容都或多或少间接和内存大小相关。这个参数最终会被设置到Executor的JVM的heap尺寸上,对应的就是Xmx和Xms的值文档
理论上Executor 内存固然是多多益善,可是实际受机器配置,以及运行环境,资源共享,JVM GC效率等因素的影响,仍是有可能须要为它设置一个合理的大小。 多大算合理,要看实际状况
Executor的内存基本上是Executor内部全部任务共享的,而每一个Executor上能够支持的任务的数量取决于Executor所管理的CPU Core资源的多少,所以你须要了解每一个任务的数据规模的大小,从而推算出每一个Executor大体须要多少内存便可知足基本的需求。
如何知道每一个任务所需内存的大小呢,这个很难统一的衡量,由于除了数据集自己的开销,还包括算法所需各类临时内存空间的使用,而根据具体的代码算法等不一样,临时内存空间的开销也不一样。可是数据集自己的大小,对最终所需内存的大小仍是有必定的参考意义的。
一般来讲每一个分区的数据集在内存中的大小,多是其在磁盘上源数据大小的若干倍(不考虑源数据压缩,Java对象相对于原始裸数据也还要算上用于管理数据的数据结构的额外开销),须要准确的知道大小的话,能够将RDD cache在内存中,从BlockManager的Log输出能够看到每一个Cache分区的大小(其实也是估算出来的,并不彻底准确)
如: BlockManagerInfo: Added rdd_0_1 on disk on sr438:41134(size: 495.3 MB)
反过来讲,若是你的Executor的数量和内存大小受机器物理配置影响相对固定,那么你就须要合理规划每一个分区任务的数据规模,例如采用更多的分区,用增长任务数量(进而须要更多的批次来运算全部的任务)的方式来减少每一个任务所需处理的数据大小。
spark.storage.memoryFraction
如前面所说spark.executor.memory决定了每一个Executor可用内存的大小,而spark.storage.memoryFraction则决定了在这部份内存中有多少能够用于Memory Store管理RDD Cache数据,剩下的内存用来保证任务运行时各类其它内存空间的须要。
spark.executor.memory默认值为0.6,官方文档建议这个比值不要超过JVM Old Gen区域的比值。这也很容易理解,由于RDD Cache数据一般都是长期驻留内存的,理论上也就是说最终会被转移到Old Gen区域(若是该RDD尚未被删除的话),若是这部分数据容许的尺寸太大,势必把Old Gen区域占满,形成频繁的FULL GC。
如何调整这个比值,取决于你的应用对数据的使用模式和数据的规模,粗略的来讲,若是频繁发生Full GC,能够考虑下降这个比值,这样RDD Cache可用的内存空间减小(剩下的部分Cache数据就须要经过Disk Store写到磁盘上了),会带来必定的性能损失,可是腾出更多的内存空间用于执行任务,减小Full GC发生的次数,反而可能改善程序运行的总体性能
spark.streaming.blockInterval
这个参数用来设置Spark Streaming里Stream Receiver生成Block的时间间隔,默认为200ms。具体的行为表现是具体的Receiver所接收的数据,每隔这里设定的时间间隔,就从Buffer中生成一个StreamBlock放进队列,等待进一步被存储到BlockManager中供后续计算过程使用。理论上来讲,为了每一个StreamingBatch 间隔里的数据是均匀的,这个时间间隔固然应该能被Batch的间隔时间长度所整除。整体来讲,若是内存大小够用,Streaming的数据来得及处理,这个blockInterval时间间隔的影响不大,固然,若是数据Cache Level是Memory+Ser,即作了序列化处理,那么BlockInterval的大小会影响序列化后数据块的大小,对于Java 的GC的行为会有一些影响。
此外spark.streaming.blockQueueSize决定了在StreamBlock被存储到BlockMananger以前,队列中最多能够容纳多少个StreamBlock。默认为10,由于这个队列Poll的时间间隔是100ms,因此若是CPU不是特别繁忙的话,基本上应该没有问题。