Spark Streaming性能调优
膜拜大佬,转载记录一下
数据接收并行度调优
-
Receiver并行化接收数据
每一个输入DStream都会在某个Worker的Executor上启动一个Receiver,可以创建多个输入DStream,并且配置它们接收数据源不同的分区数据,达到接收多个数据流的效果。
比如,一个接收两个Kafka Topic的输入DStream,可以被拆分为两个输入DStream,每个分别接收一个topic的数据。多个DStream可以使用union算子进行聚合,从而形成一个DStream。后续使用transformation算子操作int numStreams = 5; List<JavaPairDStream<String, String>> kafkaStreams = new ArrayList<JavaPairDStream<String, String>>(numStreams); for (int i = 0; i < numStreams; i++) { kafkaStreams.add(KafkaUtils.createStream(...)); } JavaPairDStream<String, String> unifiedStream = streamingContext.union(kafkaStreams.get(0), kafkaStreams.subList(1, kafkaStreams.size())); unifiedStream.print(); 复制代码 -
调节block interval
spark.streaming.blockInterval:设置block interval,默认是200ms
对于大多数Receiver来说,在将接收到的数据保存到Spark的BlockManager之前,都会将数据切分为一个一个的block。而每个batch中的block数量,则决定了该batch对应的RDD的partition的数量,以及针对该RDD执行transformation操作时,创建的task的数量。每个batch对应的task数量是大约估计的,即task ≈ batch interval / block interval,比如batch interval为2s,block interval为200ms,则创建10个task
调优目的是让batch的task数量大于每台机器的cpu core数量,尽量利用cpu core,推荐task数量是cpu core数量的2~3倍如果集群中有多个spark程序在跑,应该设置成所有应用的task数量总和是cpu core数量的2~3倍
推荐的block interval最小值是50ms,如果低于这个数值,那么大量task的启动时间,可能会变成一个性能开销点。
-
重分区
inputStream.repartition(<number of partitions>):可以将接收到的batch分布到指定数量的机器上再操作。
任务启动调优
发送task去Worker节点上的Executor的有延迟,耗时
- Task序列化:使用Kryo序列化机制来序列化task,可以减小task的大小,从而减少发送这些task到各个Worker节点上的Executor的时间
官网上并未提出这一点
- 执行模式:在Standalone/ coarse-grained Mesos模式下运行Spark,比fine-grained Mesos 模式有更少的task启动时间
These changes may reduce batch processing time by 100s of milliseconds
数据处理并行度调优
让在stage中使用的并行task的数量足够多,充分利用集群资源
如reduce,默认的并行task的数量是由spark.default.parallelism决定,为全局变量;也可以手动指定该操作的并行度
数据序列化调优
两种场景会序列化
- 默认情况下,接收到的输入数据,是存储在Executor的内存中的,使用的持久化级别是
StorageLevel.MEMORY_AND_DISK_SER_2,Receiver反序列化从网络接收到的数据,再使用Spark的序列化格式序列化数据 - 流式计算操作生成的持久化RDD会持久化到内存中,默认持久化级别是
StorageLevel.MEMORY_ONLY_SER
优化
目标:减少用于序列化和反序列化的CPU性能开销和GC开销
- 使用Kryo序列化
- 禁止序列化:数据总量并不是很多,可以将数据以非序列化的方式进行持久化
batch interval调优
batch处理时间(可以通过观察Spark UI上的batch处理时间)必须小于batch interval时间,否则会堆积数据
由于临时性的数据增长导致的暂时的延迟增长是合理的,只要延迟情况可以在短时间内恢复即可
内存调优
增大内存
在窗口时间内通过Receiver接收到的数据,会使用StorageLevel.MEMORY_AND_DISK_SER_2持久化级别来进行存储,因此无法保存在内存中的数据会溢写到磁盘上,会降低应用性能
另外,若窗口操作中要使用大量 keys 的updateStateByKey,同样会消耗大量内存
垃圾回收
- DStream的持久化
输入数据和某些操作生产的中间RDD,默认持久化时都会序列化为字节,可以使用Kryo序列化,也可以设置spark.rdd.compress=true压缩数据,代价是 CPU 时间 - 清理旧数据
默认隔一段时间会清理数据,如按窗口时间长度清理,可以使用streamingContext.remember()延长清理时间,以便给其他操作使用数据 - CMS垃圾回收器
并行的GC会降低吞吐量,但GC低开销,减少batch的处理时间- driver端
spark-submit中使用--driver-java-options:-XX:+UseConcMarkSweepGC - executor端
spark.executor.extraJavaOptions:-XX:+UseConcMarkSweepGC
- driver端
- 其他
使用 OFF_HEAP 存储级别的保持 RDDs
使用更小的 heap sizes 的 executors.这将降低每个 JVM heap 内的 GC 压力
作者:fengye
链接:https://juejin.cn/post/6844903567405350920
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

浙公网安备 33010602011771号