如何在Spark Streaming应用程序中异步写入行以加快批处理执行?

时间:2017-06-01 06:22:08

标签: performance apache-spark apache-spark-sql spark-streaming

我有一个spark作业,我需要在每个微批处理中编写SQL查询的输出。写入是一项昂贵的操作,并且导致批处理执行时间超过批处理间隔。

我正在寻找提高写作性能的方法。

  1. 正在异步执行单独线程中的写操作,如下面显示的一个好选项?

  2. 这会导致任何副作用,因为Spark本身是以分布式方式执行的吗?

  3. 还有其他/更好的方法来加快写作速度吗?

    // Create a fixed thread pool to execute asynchronous tasks
    val executorService = Executors.newFixedThreadPool(2)
    dstream.foreachRDD { rdd =>
      import org.apache.spark.sql._
      val spark = SparkSession.builder.config(rdd.sparkContext.getConf).getOrCreate
      import spark.implicits._
      import spark.sql
    
      val records = rdd.toDF("record")
      records.createOrReplaceTempView("records")
      val result = spark.sql("select * from records")
    
      // Submit a asynchronous task to write
      executorService.submit {
        new Runnable {
          override def run(): Unit = {
            result.write.parquet(output)
          }
        }
      }
    }
    

2 个答案:

答案 0 :(得分:5)

1 - 正在异步执行单独线程中的写操作,如下面显示的一个好选项?

没有。理解这个问题的关键是询问谁正在写#39;写入由在集群中的执行程序上为作业分配的资源完成。将write命令放在异步线程池上就像将新的办公室管理器添加到具有固定员工的办公室。鉴于他们必须共享相同的员工,两位经理是否能够完成比单独工作更多的工作?好吧,一个合理的答案就是"只有当第一位经理没有给他们足够的工作时才有,所以有一些免费的能力"。

回到我们的集群,我们正在处理一个对IO很重的写操作。并行化写入作业将导致争用IO资源,从而使每个独立作业更长。最初,我们的工作可能看起来比单一经理版本更好,但麻烦最终会打击我们。 我制作了一个图表,试图说明它是如何工作的。请注意,并行作业将花费更长的时间与它们在时间轴中并发的时间成比例。

sequential vs parallel jobs in Spark Streaming

一旦我们达到工作开始延迟的那一点,我们就会有一份不稳定的工作,最终会失败。

2-这会导致任何副作用,因为Spark本身是以分布式方式执行的吗?

我能想到的一些效果:

  • 可能更高的群集负载和IO争用。
  • 作业在Threadpool队列上排队,而不是在Spark Streaming Queue上排队。我们失去了通过Spark UI和监控API监控我们的工作的能力,因为延迟是隐藏的'从Spark Streaming的角度来看,一切都很好。

3-是否有其他/更好的方法可以加快写入速度? (订购从便宜到昂贵)

  • 如果要附加到镶木地板文件,请经常创建新文件。随着时间的推移,附加费用会很高。
  • 增加批处理间隔或使用Window操作编写更大的Parquet块。 Parquet喜欢大文件
  • 调整数据的分区和分配=>确保Spark可以并行写入
  • 增加群集资源,必要时添加更多节点
  • 使用更快的存储空间

答案 1 :(得分:2)

  

在一个单独的线程中异步执行写操作,如下面的一个好选项所示?

是。在优化昂贵的查询并将结果保存到外部数据存储时,当然需要考虑这一点。

  

这会导致任何副作用,因为Spark本身是以分布式方式执行的吗?

不要这么认为。 arrayTotalFeed是线程安全的,可以促进这种查询执行。

  

还有其他/更好的方法可以加快写作速度吗?

YES!这是了解何时使用其他(上述)选项的关键。默认情况下,Spark应用程序以FIFO调度模式运行。

引用Scheduling Within an Application

  

默认情况下,Spark的调度程序以FIFO方式运行作业。每个工作分为“阶段”(例如地图和减少阶段),第一个工作优先于所有可用资源,而其阶段有任务启动,然后第二个工作获得优先权等。如果工作在头部队列不需要使用整个集群,以后的作业可以立即开始运行,但如果队列头部的作业很大,则后续作业可能会显着延迟。

     

从Spark 0.8开始,还可以在作业之间配置公平共享。在公平共享下,Spark以“循环”方式在作业之间分配任务,以便所有作业获得大致相等的群集资源份额。这意味着在长时间工作运行时提交的短工作可以立即开始接收资源,并且仍然可以获得良好的响应时间,而无需等待长时间的工作完成。此模式最适合多用户设置。

这意味着要为异步和并行执行多个写入腾出空间,您应该将Spark应用程序配置为使用 FAIR调度模式(使用SparkContext属性)。

您必须配置所谓的Fair Scheduler Pools到"分区"执行程序资源(CPU和内存)到可以使用spark.scheduler.mode属性分配给作业的池中。

引用Fair Scheduler Pools

  

如果没有任何干预,新提交的作业将进入默认池,但可以通过添加spark.scheduler.pool"本地属性"来设置作业池。到提交它们的线程中的SparkContext。