Spark流式计算程序要想运行顺畅,也需要一些基本的调优,总结一下主要在两个方向:
- 每个批次的处理时间尽可能短。
- 收到数据后,尽可能地处理。
1.减少批处理的时间
一是增加数据接收的并发数量,尤其是当瓶颈发生在数据接收的时候。默认每个Input DStream都只会创建一个接收器,运行在某个节点上,我们可以创建多个Input DStream,让它们接收不同的数据分区,以实现并行接收。比如一个接收两个Kafka topic的Input DStream可以优化为两个Input DStream,各接收一个topic,然后再合并。代码如下:
val numStreams = 5
val kafkaStreams = (1 to numStreams).map{i => KafkaUtils.createStream(...)}
val unifiedStream = streamContext.union(kafkaStreams)
unifiedStream.print()
二是数据处理的并发度,如果并发度不够,可能导致集群的资源不被充分利用。一个最简单的方法是看各机器CPU的所有核心是不是都在工作,如果有空闲的,则可以考虑增加并行度【可以调整选项:spark.default.parallelism】。
三要数据序列化,数据接收后,当需要与磁盘交换数据时,数据可能会进行序列化和反序列化,好处是节省空间和内存,但会增加计算负担。因此,我们应该尽可能地使用Kryo来完成这项工作,CPU和内存开销都相对小一些。。
最后是要注意task启动的额外开销,如果task启动过于频繁,那么额外的开销可能非常高,甚至无法达到那样的实时计算要求。
2.设置合理批次间隔时间
为了让每个批次的数据能够尽快处理,批次间隔时间的设置非常重要。经验表明,一般来说短时间间隔会导致更多的额外开销,以及无法完成的风险,所以前期可以采用相对保守的方法,比如设置间隔为5~10秒。然后,通过观察运行数据或者最终的输出数据确保系统足够实时,每个间隔的实际计算时间远小于间隔时间,然后再逐渐按需要缩短间隔时间。