作者:刘旭晖 Raymond 转载请注明出处
Email:colorant at 163.com
BLOG:http://blog.csdn.net/colorant/
随着Spark的逐渐成熟完好, 越来越多的可配置參数被加入到Spark中来, 本文试图通过阐述这当中部分參数的工作原理和配置思路, 和大家一起探讨一下怎样依据实际场合对Spark进行配置优化。
因为篇幅较长。所以在这里分篇组织,假设要看最新完整的网页版内容。能够戳这里:http://spark-config.readthedocs.org/,主要是便于更新内容
schedule调度相关
调度相关的參数设置,大多数内容都非常直白,事实上无须过多的额外解释。只是基于这些參数的经常使用性(大概会是你针对自己的集群第一步就会配置的參数),这里多少就其内部机制做一些解释。
spark.cores.max
一个集群最重要的參数之中的一个。当然就是CPU计算资源的数量。spark.cores.max 这个參数决定了在Standalone和Mesos模式下,一个Spark应用程序所能申请的CPU Core的数量。假设你没有并发跑多个Spark应用程序的需求,那么能够不须要设置这个參数,默认会使用spark.deploy.defaultCores的值(而spark.deploy.defaultCores的值默觉得Int.Max,也就是不限制的意思)从而应用程序能够使用全部当前能够获得的CPU资源。
针对这个參数须要注意的是,这个參数对Yarn模式不起作用,YARN模式下,资源由Yarn统一调度管理,一个应用启动时所申请的CPU资源的数量由另外两个直接配置Executor的数量和每一个Executor中core数量的參数决定。(历史原因造成,不同执行模式下的一些启动參数个人觉得还有待进一步整合)
此外,在Standalone模式等后台分配CPU资源时,眼下的实现中,在spark.cores.max同意的范围内。基本上是优先从每一个Worker中申请所能得到的最大数量的CPU core给每一个Executor。因此假设人工限制了所申请的Max Core的数量小于Standalone和Mesos模式所管理的CPU数量。可能发生应用仅仅执行在集群中部分节点上的情况(因为部分节点所能提供的最大CPU资源数量已经满足应用的要求),而不是平均分布在集群中。通常这不会是太大的问题,可是假设涉及数据本地性的场合,有可能就会带来一定的必须进行远程数据读取的情况发生。
理论上,这个问题能够通过两种途径解决:一是Standalone和Mesos的资源管理模块自己主动依据节点资源情况,均匀分配和启动Executor。二是和Yarn模式一样,同意用户指定和限制每一个Executor的Core的数量。
社区中有一个PR试图走另外一种途径来解决相似的问题,只是截至我写下这篇文档为止(2014.8),还没有被Merge。
spark.task.cpus
这个參数在字面上的意思就是分配给每一个任务的CPU的数量,默觉得1。实际上。这个參数并不能真的控制每一个任务实际执行时所使用的CPU的数量,比方你能够通过在任务内部创建额外的工作线程来使用很多其他的CPU(至少眼下为止,将来任务的执行环境能否通过LXC等技术来控制还不好说)。它所发挥的作用,仅仅是在作业调度时,每分配出一个任务时,对已使用的CPU资源进行计数。也就是说仅仅是理论上用来统计资源的使用情况,便于安排调度。因此,假设你期望通过改动这个參数来加快任务的执行,那还是赶紧换个思路吧。这个參数的意义。个人觉得还是在你真的在任务内部自己通过不论什么手段,占用了很多其他的CPU资源时。让调度行为更加准确的一个辅助手段。
spark.scheduler.mode
这个參数决定了单个Spark应用内部调度的时候使用FIFO模式还是Fair模式。是的,你没有看错。这个參数仅仅管理一个Spark应用内部的多个没有依赖关系的Job作业的调度策略。
假设你须要的是多个Spark应用之间的调度策略,那么在Standalone模式下,这取决于每一个应用所申请和获得的CPU资源的数量(临时没有获得资源的应用就Pending在那里了),基本上就是FIFO形式的。谁先申请和获得资源,谁就占用资源直到完毕。而在Yarn模式下。则多个Spark应用间的调度策略由Yarn自己的策略配置文件所决定。
那么这个内部的调度逻辑有什么用呢?假设你的Spark应用是通过服务的形式,为多个用户提交作业的话,那么能够通过配置Fair模式相关參数来调整不同用户作业的调度和资源分配优先级。
spark.locality.wait
spark.locality.wait和spark.locality.wait.process,spark.locality.wait.node, spark.locality.wait.rack这几个參数影响了任务分配时的本地性策略的相关细节。
Spark中任务的处理须要考虑所涉及的数据的本地性的场合,基本就两种,一是数据的来源是HadoopRDD; 二是RDD的数据来源来自于RDD Cache(即由CacheManager从BlockManager中读取,或者Streaming数据源RDD)。
其他情况下。假设不涉及shuffle操作的RDD。不构成划分Stage和Task的基准,不存在推断Locality本地性的问题,而假设是ShuffleRDD,其本地性始终为No Prefer。因此事实上也无所谓Locality。
在理想的情况下。任务当然是分配在能够从本地读取数据的节点上时(同一个JVM内部或同一台物理机器内部)的执行时性能最佳。
可是每一个任务的执行速度无法准确预计,所以非常难在事先获得全局最优的执行策略,当Spark应用得到一个计算资源的时候。假设没有能够满足最佳本地性需求的任务能够执行时,是退而求其次。执行一个本地性条件稍差一点的任务呢。还是继续等待下一个可用的计算资源已期望它能更好的匹配任务的本地性呢?
这几个參数一起决定了Spark任务调度在得到分配任务时,选择临时不分配任务,而是等待获得满足进程内部/节点内部/机架内部这种不同层次的本地性资源的最长等待时间。
默认都是3000毫秒。
基本上。假设你的任务数量较大和单个任务执行时间比較长的情况下,单个任务是否在数据本地执行,代价差别可能比較显著,假设数据本地性不理想。那么调大这些參数对于性能优化可能会有一定的优点。反之假设等待的代价超过带来的收益,那就不要考虑了。
特别值得注意的是:在处理应用刚启动后提交的第一批任务时,因为当作业调度模块開始工作时,处理任务的Executors可能还没有全然注冊完毕。因此一部分的任务会被放置到No Prefer的队列中。这部分任务的优先级仅次于数据本地性满足Process级别的任务,从而被优先分配到非本地节点执行。假设的确没有Executors在相应的节点上执行,或者的确是No Prefer的任务(如shuffleRDD)。这样做确实是比較优化的选择。可是这里的实际情况仅仅是这部分Executors还没来得及注冊上而已。这种情况下,即使加大本节中这几个參数的数值也没有帮助。针对这个情况。有一些已经完毕的和正在进行中的PR通过比如动态调整No Prefer队列,监控节点注冊比例等等方式试图来给出更加智能的解决方式。只是,你也能够依据自身集群的启动情况。通过在创建SparkContext之后,主动Sleep几秒的方式来简单的解决问题。
spark.speculation
spark.speculation以及spark.speculation.interval,spark.speculation.quantile, spark.speculation.multiplier等參数调整Speculation行为的详细细节,Speculation是在任务调度的时候,假设没有适合当前本地性要求的任务可供执行。将跑得慢的任务在空暇计算资源上再度调度的行为。这些參数调整这些行为的频率和推断指标,默认是不使用Speculation的。
通常来说非常难正确的推断是否须要Speculation,能真正发挥Speculation用处的场合。往往是某些节点因为执行环境原因。比方CPU资源因为某种原因被占用。磁盘损坏导致IO缓慢造成任务执行速度异常的情况,当然前提是你的分区任务不存在仅能被执行一次,或者不能同一时候执行多个拷贝等情况。Speculation任务參照的指标一般是其他任务的执行时间。而实际的任务可能因为分区数据尺寸不均匀,本来就会有时间差异,加上一定的调度和IO的随机性,所以假设一致性指标定得过严,Speculation可能并不能真的发现问题,反而添加了不必要的任务开销,定得过宽。大概又基本相当于没用。
个人觉得。假设你的集群规模比較大,执行环境复杂,的确可能经常发生执行异常,加上数据分区尺寸差异不大。为了程序执行时间的稳定性,那么能够考虑细致调整这些參数。否则还是考虑怎样排除造成任务执行速度异常的因数比較靠铺一些。
当然,我没有实际在非常大规模的集群上执行过Spark,所以假设看法有些偏颇。还请有实际经验的XD指正。