Spark Streaming之性能调优

46 篇文章 3 订阅
41 篇文章 1 订阅

一.简介

要在集群上的Spark Streaming应用程序中获得最佳性能,需要进行一些调整。可以调整以提高应用程序性能的许多参数和配置。从高层次上讲,需要考虑两件事:

  • 通过有效地使用群集资源来减少每批数据的处理时间。
  • 设置正确的批处理大小,以便可以按接收到的批处理速度处理一批数据【也就是说,数据处理跟上数据摄取的速度】。

由于大多数Spark计算的内存性质,Spark程序可能会受到群集中任何资源【CPU,网络带宽或内存】的瓶颈。通常,如果数据适合内存,则瓶颈是网络带宽,但是有时,还需要进行一些调整,例如以序列化形式存储RDD,以减少内存使用量。数据序列化,这对于良好的网络性能至关重要,并且还可以减少内存使用。

二.数据接收中的并行度

通过网络【例如Kafka,套接字等】接收数据需要对数据进行反序列化并将其存储在Spark中。如果数据接收成为系统的瓶颈,请考虑并行化数据接收。请注意,每个输入DStream都会创建一个接收器【在工作节点上运行】,该接收器接收单个数据流。因此,可以通过创建多个输入DStream并将其配置为从源接收数据流的不同分区来实现接收多个数据流。例如,可以将接收两个主题数据的单个Kafka输入DStream拆分为两个Kafka输入流,每个输入流仅接收一个主题。这将运行两个接收器,从而允许并行接收数据,从而提高了总体吞吐量。可以将这些多个DStream组合在一起以创建单个DStream。然后,可以将应用于单个输入DStream的转换应用于统一流。如下。

val numStreams = 5
val kafkaStreams = (1 to numStreams).map { i => KafkaUtils.createStream(...) }
val unifiedStream = streamingContext.union(kafkaStreams)
unifiedStream.print()

应考虑的另一个参数是接收机的块间隔,该间隔由配置参数spark.streaming.blockInterval确定。对于大多数接收器,接收到的数据在存储到Spark内存中之前会合并为数据块。每批中的块数确定了将在类似map的转换中用于处理接收到的数据的任务数。每批接收器中每个接收器的任务数大约为【批处理间隔/块间隔】。例如,200 ms的块间隔将每2秒批处理创建10个任务。如果任务数太少【即少于每个节点的核心数】,那么它将效率低下,因为没有使用所有可用的核心来处理数据。要增加给定批处理间隔的任务数,减小块间隔。但是,建议的块间隔最小值约为50毫秒,在此之下,任务启动开销可能是个问题。

使用多个输入流/接收器接收数据的另一种方法是显式地对输入数据流进行分区【使用inputStream.repartition()】。在进一步处理之前,这会将接收到的数据批分发到集群中指定数量的计算机上。

三.数据处理中的并行度

如果在计算的任何阶段使用的并行任务数量不够高,则集群资源可能无法得到充分利用。例如,对于像reduceByKey 和reduceByKeyAndWindow 这样的分布式归约操作,并行任务的默认数量由spark.default.parallelism 配置属性控制。可以将并行性级别作为参数传递,或将spark.default.parallelism 配置属性设置为更改默认值。

四.数据序列化

可以通过调整序列化格式来减少数据序列化的开销。在流传输的情况下,有两种类型的数据被序列化。

  • 输入数据:默认情况下,通过Receivers接收到的输入数据通过StorageLevel.MEMORY_AND_DISK_SER_2存储在执行程序的内存中。也就是说,数据被序列化为字节以减少GC开销,并被复制以容忍执行器故障。同样,数据首先保存在内存中,并且仅在内存不足以容纳流计算所需的所有输入数据时才溢出到磁盘。显然,这种序列化会产生开销,接收器必须对接收到的数据进行反序列化,然后使用Spark的序列化格式对其进行重新序列化。
  • 流操作生成的持久RDD:流计算生成的RDD可以保存在内存中。例如,窗口操作会将数据保留在内存中,因为它们将被多次处理。但是,与Spark Core默认的StorageLevel.MEMORY_ONLY不同,默认情况下,由流计算生成的持久性RDD会与StorageLevel.MEMORY_ONLY_SER(即序列化)保持一致,以最大程度地减少GC开销。

在这两种情况下,使用Kryo序列化可以减少CPU和内存的开销。对于Kryo,请考虑注册自定义类,并禁用对象引用跟踪中与Kryo相关的配置。

在流应用程序需要保留的数据量不大的特定情况下,将数据(两种类型)持久化为反序列化对象是可行的,而不会产生过多的GC开销。例如,如果使用的是几秒钟的批处理间隔并且没有窗口操作,那么可以尝试通过显式设置存储级别来禁用持久化数据中的序列化。这将减少由于序列化导致的CPU开销,从而可能在没有太多GC开销的情况下提高性能。
将对象序列化为慢速格式或占用大量字节的格式将大大减慢计算速度。通常,这是应该优化Spark应用程序的第一件事。Spark旨在在便利性【允许在操作中使用任何Java类型】和性能之间取得平衡。它提供了两个序列化库:

  • Java序列化:默认情况下,Spark使用Java的ObjectOutputStream框架对对象进行序列化,并且可以与创建的实现类一起使用 java.io.Serializable。还可以通过java.io.Externalizable扩展来更紧密地控制序列化的性能。Java序列化很灵活,但是通常很慢,并且导致许多类的序列化格式很大。
  • Kryo序列化:Spark还可以使用Kryo库(版本4)更快地序列化对象。与Java序列化【通常多达10倍】相比,Kryo显着更快,更紧凑,但是Kryo不支持所有 Serializable类型,并且需要预先注册要在程序中使用的类才能获得最佳性能。

可以通过使用SparkConf初始化作业,并调用conf.set(“spark.serializer”, “org.apache.spark.serializer.KryoSerializer”)来切换为使用Kryo。此设置配置了串行器,该串行器不仅用于在工作程序节点之间改组数据,而且还用于将RDD序列化到磁盘。Kryo不是默认值的唯一原因是由于自定义注册要求,但是我们建议在任何网络密集型应用程序中尝试使用它。从Spark 2.0.0开始,在将RDD与简单类型,简单类型的数组或字符串类型进行混洗时,我们在内部使用Kryo序列化器。

Spark自动为Twitter chill库的AllScalaRegistrar中涵盖的许多常用Scala核心类使用Kryo序列化器。

要向Kryo注册自己的自定义类,请使用registerKryoClasses方法。

val conf = new SparkConf().setMaster(...).setAppName(...)
conf.registerKryoClasses(Array(classOf[MyClass1], classOf[MyClass2]))
val sc = new SparkContext(conf)

如果对象很大,则可能还需要增加配置spark.kryoserializer.buffer。该值必须足够大以容纳要序列化的最大对象。

最后,如果不注册自定义类,Kryo仍然可以工作,但必须将完整的类名与每个对象一起存储,这很浪费。

五.任务启动开销

如果每秒启动的任务数量很高【例如每秒50个或更多】,则向服务器发送任务的开销可能会很大,并且将难以实现亚秒级的延迟。可以通过以下更改来减少开销:

  • 执行模式:在独立模式或粗粒度Mesos模式下运行Spark可以比细粒度Mesos模式缩短任务启动时间。

这些更改可以将批处理时间减少100毫秒,从而使亚秒级的批处理大小成为可行。

六.设置正确的批次间隔

为了使在集群上运行的Spark Streaming应用程序稳定,系统应能够尽快处理接收到的数据。换句话说,应尽快处理成批的数据。可以通过监视流式Web UI中的处理时间来发现这是否适用于应用程序 ,其中批处理时间应小于批处理间隔。

根据流计算的性质,所使用的批处理间隔可能会对数据速率产生重大影响,该速率可以由应用程序在一组固定的集群资源上维持。例如,让我们考虑较早的WordCountNetwork示例。对于特定的数据速率,系统可能能够跟上每2秒(即2秒的批处理间隔)而不是每500毫秒报告字数的问题。因此,需要设置批次间隔,以便可以维持生产中的预期数据速率。

找出适合的应用程序的正确批处理大小的一种好方法是使用保守的批处理间隔(例如5-10秒)和低数据速率进行测试。要验证系统是否能够跟上数据速率,可以检查每个已处理批次经历的端到端延迟的值【可以在Spark驱动程序log4j日志中查找“总延迟”,也可以使用 流侦听器接口】。如果延迟保持与批次大小相当,则系统是稳定的。否则,如果延迟持续增加,则意味着系统无法跟上,因此不稳定。一旦有了稳定配置的想法,就可以尝试提高数据速率或减小批处理大小。注意,由于暂时的数据速率增加而引起的延迟的瞬时增加可能很好,只要延迟减小回到较低的值即可。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值