Spark 性能相关参数配置详解
*压缩与序列化篇*
spark.serializer -> 选取序列化器
默认为org.apache.spark.serializer.JavaSerializer -> 可选org.apache.spark.serializer.KryoSerializer
=> 这里可配的Serializer针对的对象是Shuffle数据,以及RDD Cache等场合
=> Spark Task的序列化是通过spark.closure.serializer来配置,但是目前只支持JavaSerializer
spark.broadcast.compress --是否对Broadcast数据进行压缩
对Broadcast的数据进行压缩,默认值为True
Broadcast机制是用来减少运行每个Task时,一个Executor只需要在第一个Task启动时,获得一份Broadcast数据,之后的Task都从本地的BlockManager中获取相关数据.
1.1中,RDD本身也改为以Broadcast的形式发送给Executor(之前的实现RDD本身是随每个任务发送的)
什么情况可能不压缩更好呢,在网络带宽和内存不是问题,且Driver端CPU资源很成问题(毕竟压缩的动作基本都在Driver端执行),那或许有调整的必要。
spark.rdd.compress --> rdd 序列化后,是否再压缩
RDD Cache的过程中,RDD数据在序列化之后是否进一步进行压缩再储存到内存或磁盘上.
对于Cache在磁盘上而言,绝对大小大概没有太大关系,主要是考虑Disk的IO带宽
而对于Cache在内存中,那主要就是考虑尺寸的影响,是否能够Cache更多的数据,是否能减小Cache数据对GC造成的压力
这个值默认是关闭的
如果在磁盘IO的确成为问题或者GC问题真的没有其它更好的解决办法的时候,可以考虑启用RDD压缩。
spark.io.compression.codec --> 如果压缩的话,采用什么压缩算法.
RDD Cache和Shuffle数据压缩所采用的算法Codec,曾经是使用LZF作为默认Codec,最近因为LZF的内存和CPU开销的问题,默认的Codec已经改为Snappy.
Shuffle
使用HashShuffleManager时可能成为问题,因为如果Reduce分区数量巨大,需要同时打开大量的压缩数据流用于写文件.
使用SortShuffleManager,由于shuffle文件数量大大减少,不会产生大量的压缩数据流,所以内存开销大概不会成为主要问题。
个人认为CPU通常更容易成为瓶颈,所以要调整性能,要不不压缩,要不使用Snappy可能性大一些!
*Shuffle 相关*
Shuffle操作大概是对Spark性能影响最大的步骤之一(因为可能涉及到排序,磁盘IO,网络IO等众多CPU或IO密集的操作),所以在 1.1进行了重构。
spark.shuffle.manager
HashShuffleManager
问题:如果Reduce分区的数量比较大,将会产生大量的磁盘文件.此外由于同时打开的文件句柄数量众多,序列化,压缩,对内存的使用和GC带来很大的压力.
SortShuffleManager(default)
同一个Map任务Shuffle到不同的Reduce分区中去的所有数据都可以写入到同一个外部磁盘文件,用Offset标志不同Reduce分区的偏移量
->选择
如果不需要sort的shuffle,如果文件数量不是特别巨大,HashShuffleManager面临的内存问题不大。而SortShuffleManager需要额外的根据Partition进行排序。
如果本来就需要在Map端进行排序的Shuffle,SortShuffleManager
spark.shuffle.sort.bypassMergeThreshold
仅限于 SortShuffleManager,小于设置数,不使用Merge Sort的方式处理数据,与Hash Shuffle类似,直接将分区文件写入单独的文件,最后合并文件,本质上是相对HashShuffleMananger一个折衷方案.
spark.shuffle.consolidateFiles
仅适于HashShuffleMananger,为了解决生成过多文件的问题.涉及到底层具体的文件系统的实现和限制等因素,一直有bug.
spark.shuffle.spill
默认打开
内存不够用怎么办,一out of memory 出错了,
二就是将部分数据临时写到外部存储设备中去,最后再合并到最终的Shuffle输出文件中去
spark.shuffle.memoryFraction / spark.shuffle.safetyFraction
前者(1.1后默认为0.2)决定了当Shuffle过程中使用的内存达到总内存多少比例的时候开始Spill
由于Shuffle数据的大小是估算出来的.
后者是到了这个阀值,一定要spill,要不然就悲剧了。
spark.shuffle.spill.compress / spark.shuffle.compress
Shuffle过程中是否使用压缩算法对Shuffle数据进行压缩,默认都是True
前者针对Spill的中间数据,
后者针对最终的shuffle输出文件,
Shuffle过程中数据是否应该压缩,取决于CPU/DISK/NETWORK的实际能力和负载,应该综合考虑
*Storage相关配置参数*
spark.local.dir
Spark用于写中间数据,如RDD Cache,Shuffle,Spill等数据的位置
1.我们可以配置多个路径(用逗号分隔)到多个磁盘上增加整体IO带宽
2.如果存储设备有快有慢,可以通过在快的设备上配置更多的目录路径来增大它被Spark使用的比例。
终级方案:像目前HDFS的实现方向一样,让Spark能够感知具体的存储设备类型,针对性的使用。
1.0后 SPARK_LOCAL_DIRS (Standalone, Mesos) or LOCAL_DIRS (YARN) 覆盖这个配置,最终依赖于 cluster-manager(standalone,yarn,mesos) 的设置
spark.executor.memory
默认值为0.6,官方文档建议这个比值不要超过JVM Old Gen区域的比值.
这个参数最终会被设置到Executor的JVM的heap尺寸上,对应的就是Xmx和Xms的值。
Executor的内存基本上是Executor内部所有任务共享的,需要了解每个任务的数据规模的大小,从而推算出每个Executor大致需要多少内存
任务所需内存 = 数据集本身(主要依据) + 算法的临时内存空间
spark.storage.memoryFraction
default 0.6 -> Cache数据的大小,剩下的用来保证任务运行。
spark.streaming.blockInterval
Spark Streaming里Stream Receiver生成Block的时间间隔,默认200ms.每隔200ms,就从Buffer中生成一个StreamBlock放进队列.等待进一步被存储到BlockManager中供后续计算过程使用.
spark.streaming.blockQueueSize
*schedule调度相关*
spark.cores.max
决定了在Standalone和Mesos模式下,一个Spark应用程序所能申请的CPU Core的数量.
Yarn模式不起作用,YARN模式下,资源由Yarn统一调度管理,一个应用启动时所申请的CPU资源的数量由Executor的数量和每个Executor中core数量的参数决定
Standalone中,基本上是优先从每个Worker中申请所能得到的最大数量的CPU core给每个Executor.
spark.task.cpus
分配给每个任务的CPU的数量,默认为1,并不能真的控制每个任务实际运行时所使用的CPU的数量.
spark.scheduler.mode
单个Spark应用内部调度的时候使用FIFO模式还是Fair模式.只管理一个Spark应用内部的多个没有依赖关系的Job作业的调度策略.
在Standalone模式下,取决于每个应用所申请和获得的CPU资源的数量,基本是FIFO。
在Yarn模式,多个Spark应用间的调度策略由Yarn自己的策略配置文件所决定.
spark.locality.wait
spark.locality.wait和
spark.locality.wait.process,
spark.locality.wait.node,
spark.locality.wait.rack
这几个参数影响了任务分配时的本地性策略的相关细节。
数据本地性的场合:
1.数据的来源是HadoopRDD
2.RDD的数据来源来自于RDD Cache(即由CacheManager从BlockManager中读取,或者Streaming数据源RDD).
其它情况下,如果不涉及shuffle操作的RDD,不构成划分Stage和Task的基准(shuffle是stage的分界线),不存在判断Locality本地性的问题.
而如果是ShuffleRDD,其本地性始终为No Prefer,因此其实也无所谓Locality。
当Spark应用得到一个计算资源时,如果没有满足最佳本地性需求的任务可以运行,那是
1.运行一个本地性条件稍差一点的任务呢
2.还是继续等待下一个或许能够满足最佳本地性的计算资源呢?
这几个参数一起决定了Spark任务调度在得到分配任务时,选择暂时不分配任务,
而是等待获得满足进程内部/节点内部/机架内部这样的不同层次的本地性资源的最长等待时间.默认都是3000毫秒.