我有一个spark作业,我需要在每个微批处理中编写SQL查询的输出。写入是一项昂贵的操作,并且导致批处理执行时间超过批处理间隔。
我正在寻找提高写作性能的方法。
正在异步执行单独线程中的写操作,如下面显示的一个好选项?
这会导致任何副作用,因为Spark本身是以分布式方式执行的吗?
还有其他/更好的方法来加快写作速度吗?
// 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)
}
}
}
}
答案 0 :(得分:5)
1 - 正在异步执行单独线程中的写操作,如下面显示的一个好选项?
没有。理解这个问题的关键是询问谁正在写#39;写入由在集群中的执行程序上为作业分配的资源完成。将write命令放在异步线程池上就像将新的办公室管理器添加到具有固定员工的办公室。鉴于他们必须共享相同的员工,两位经理是否能够完成比单独工作更多的工作?好吧,一个合理的答案就是"只有当第一位经理没有给他们足够的工作时才有,所以有一些免费的能力"。
回到我们的集群,我们正在处理一个对IO很重的写操作。并行化写入作业将导致争用IO资源,从而使每个独立作业更长。最初,我们的工作可能看起来比单一经理版本更好,但麻烦最终会打击我们。 我制作了一个图表,试图说明它是如何工作的。请注意,并行作业将花费更长的时间与它们在时间轴中并发的时间成比例。
一旦我们达到工作开始延迟的那一点,我们就会有一份不稳定的工作,最终会失败。
2-这会导致任何副作用,因为Spark本身是以分布式方式执行的吗?
我能想到的一些效果:
3-是否有其他/更好的方法可以加快写入速度? (订购从便宜到昂贵)
答案 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
属性分配给作业的池中。
如果没有任何干预,新提交的作业将进入默认池,但可以通过添加
spark.scheduler.pool
"本地属性"来设置作业池。到提交它们的线程中的SparkContext。