背景
工作中使用Spark Streaming处理实时数据流,发现所处理的数据量与所消耗的时间很不对等,如下图:
区区几KB的数据,简单的mapToPair操作,竟然耗时4~5秒,很不合理。
于是,点击进去看Stage详情:
可见所有Task的耗时都是毫秒级的,怎么整个Stage就需要4秒呢?
通过查看EventTimeline,发现竟然有3秒的空窗期,这3秒内,没有任何Task再执行,而正是这3秒导致整个Stage的耗时增加。
解决方法
启动Spark任务时,设置参数spark.locality.wait=0s
即可。
什么是 locality wait ?
为什么会等待3秒呢?
原来这是Spark的一个任务管理策略。Spark把Stage拆解成N个Task,那么这N个Task要交给哪些节点去处理,就有说法了。
考虑到不同节点之间数据转移、复制的带宽成本会比较高,所以应尽量避免数据在不同节点之间流转。那对应的策略就是:数据在哪个节点上,就把Task分配到对应的节点上,这样就避免了不必要的网络传输。
那么,这就引出另外一个问题,数据所在的节点比较忙,压力比较大,没有资源来执行你分配的任务怎么办?
Spark的策略是:等你一会儿。
这就是spark.locality.wait
参数的含义了。这个参数默认值是3秒,那么就会等待数据节点3秒钟。在3秒内数据节点有资源并且成功创建了任务,那么就省去了网络传输;如果3秒内没有创建成功,那么就把任务分派给其他有资源的节点去完成了。
如何调优?
等待3秒,这是Spark的默认策略。实际应用中,我们应该根据具体的数据情况来适当调整这个参数。
以本文背景为例,每次处理就区区几KB的数据,就算走网络和很快就完成了,肯定是远小于3秒,那么这种情况就可以调到0秒,也就是不要管带宽压力,直接给按资源情况分配任务。
而如果这个数据很大,网络传输的话耗时远超3秒,这是可以适当调大spark.locality.wait,已避免网络传输。
按照官方的建议,如果Jobs处理时间很短(小于2秒),就把这个值调小,甚至是0;如果Jobs处理时间很长,也不在乎多等几秒,那么就适当调大。