我在4个CPU内核和8个线程上使用Spark 2.4.0和Scala 2.11。
我编写了以下应用程序:
package demos.spark
object WordCounter {
def main(args: Array[String]): Unit = {
import org.apache.spark.sql.SparkSession
val spark = SparkSession
.builder
.master("local[4]")
.getOrCreate
import spark.implicits._
spark
.readStream
.format("socket")
.option("host", "localhost")
.option("port", 9999)
.load
.as[String]
.flatMap(_.split("\\W+"))
.groupBy("value")
.count
.writeStream
.outputMode("complete")
.format("console")
.start
.awaitTermination
}
}
使用local[1]
的应用程序的处理时间约为60秒。 local[8]
下降到约15秒,这是我获得的最小值。
我总是通过套接字发送一两个句子作为输入。
这是预期的行为吗?如何优化应用程序以使其具有1秒的处理时间?
编辑: 经过长时间的研究,终于找到了解决方案。问题出在Spark默认使用的分割过多(几百个)。添加spark.sql.shuffle.partitions选项设置为8(我的机器上的内核数)后,数据处理的持续时间已降至300-400毫秒
val spark = SparkSession
.builder
.master("local[*]")
.config("spark.sql.shuffle.partitions", 8)
.getOrCreate
我还不知道这个数字是否应该保持不变,如果Spark应用程序将在可能正在发生变化的基础架构(Spark,Kubernetes,AWS,自动缩放)上运行,该怎么办?
答案 0 :(得分:0)
4个CPU内核和8个线程。
使用local[*]
,Spark将使用与内核一样多的处理线程。即4。如果这8个线程是虚拟内核,Spark将看到8个“ CPU内核”,因此8是最大线程数。处理。
这正是您的测试所证明的,即
local[8]
下降到约15秒,这是我获得的最小值。这是预期的行为吗?
是的,除非您更改处理逻辑(即结构化查询本身),否则几乎不可能浪费时间。那就是我通常说的考虑算法的地方(每个要处理的数据可能有所不同)。您受到可用CPU内核数量的限制。
如何优化应用程序以使其具有1秒的处理时间?
更改结构化查询(“算法”)或其幕后工作方式。
以下操作是处理逻辑:
.flatMap(_.split("\\W+"))
.groupBy("value")
.count
flatMap
价格便宜,并且可以获得与CPU内核一样快的速度。您对此无能为力。
您还可以使用流式聚合groupBy
后跟count
来更改执行所需的任务数(在您的情况下,该数目将从8更改为默认的随机排序分区数,即200 )。
您可以计算在8个内核上运行200个任务所需的CPU滴答次数,您将需要大量时间来计算结果。
问题出在Spark缺省使用的分割过多(几百个)。添加spark.sql.shuffle.partitions选项设置为8(我的机器上的内核数)后,数据处理的持续时间已降至300-400毫秒
当然可以,在您遇到的这种情况下,这是有帮助的,如果那是您可能拥有的唯一硬件,那就可以了。完成了。
在其他环境中内核数量可能会更高的情况如何?
如果该数字是否恒定,那么Spark应用程序将在可能发生变化的基础架构(Spark,Kubernetes,AWS,自动缩放)上运行怎么办?
这是最难回答的问题。欢迎来到非常动态/高度可配置的Apache Spark世界。有太多因素会影响最终结果,通常您所拥有的是最终的结果,或者开始调整many configuration options,您将不得不花费数小时或数周的时间来确定最佳配置。考虑一下您的流查询将处理的不同数据(数据形状,体积和速度)。加起来很混乱。
戴上咨询帽,在某个时候,您将不得不决定应用程序性能是否足够好,或者您将花费数周的时间来希望自己做得比已经达到的更好(并且有人必须为此付出代价) )。
此数字是否应保持不变
如果您知道将要处理的所有数据,则可以做出如此艰难的假设。
一般来说不应该,这就是Spark为您提供Adaptive Query Execution(video)的原因。