Scala spark分区中的数据何时实际实现?
我正在分析spark应用程序在小数据集情况下的性能。我有一个沿袭图,它看起来像下面这样:Scala spark分区中的数据何时实际实现?,scala,apache-spark,Scala,Apache Spark,我正在分析spark应用程序在小数据集情况下的性能。我有一个沿袭图,它看起来像下面这样: someList.toDS() .repartition(x) .mapPartitions(func1) .mapPartitions(func2) .mapPartitions(func3) .filter(cond1) .count() 我有一个由2个节点组成的集群,每个节点上有8个核心。执行器配置为使用4个内核。因此,当应用程序运行时,会出现四个执行器,每个执行器使用4个内核 我在每个线程上观察到
someList.toDS()
.repartition(x)
.mapPartitions(func1)
.mapPartitions(func2)
.mapPartitions(func3)
.filter(cond1)
.count()
我有一个由2个节点组成的集群,每个节点上有8个核心。执行器配置为使用4个内核。因此,当应用程序运行时,会出现四个执行器,每个执行器使用4个内核
我在每个线程上观察到至少(通常仅)一个任务(即总共16个任务)比其他任务花费的时间要长得多。例如,与在一秒钟或更短时间内运行的其他任务相比,在一次运行中,这些任务大约需要15-20秒
在分析代码时,我发现瓶颈在上面的func3中:
def func3 = (partition: Iterator[DomainObject]) => {
val l = partition.toList // This takes almost all of the time
val t = doSomething(l)
}
从迭代器到列表的转换几乎占用了所有的时间
分区大小非常小(在某些情况下甚至小于50)。即使这样,分区的大小在不同的分区之间几乎是一致的,但是每个线程只有一个任务占用时间
我假设当func3
在任务的执行器上运行时,该分区中的数据已经存在于执行器上。不是这样吗?(在执行func3的过程中,它是否在整个数据集上迭代以过滤掉该分区的数据?!)
否则,为什么从一个不到50个对象的迭代器
到一个列表
的转换会占用那么多时间
我注意到的另一件事(不确定这是否相关)是这些任务的GC时间(根据spark ui),与其他任务相比,这16项任务的GC时间也异常一致2秒(即使如此,2秒)
重新分区()期间
filter
操作之后,所有三个mapPartitions
开始执行(当调用count
操作时)。这取决于每个函数中的doSomething()
将取决于DAG的创建方式和所花的时间以及相应的优化分区中的数据似乎在任务开始执行时就可用(或者,正如问题所显示的那样,至少在迭代该数据时没有任何显著的成本) 上面代码中的瓶颈实际上在func2中(我没有正确地研究过!),这是因为scala中迭代器的惰性。这个问题与spark完全无关 首先,上面mapPartitions调用中的函数似乎被链接起来,并像这样调用:
func3(func2(func1(迭代器[A])):迭代器[B]
。因此,作为func2
输出的迭代器
直接输入到func3
其次,对于上述问题,func1
(和func2
)定义为:
func1(x: Iterator[A]) => Iterator[B] = x.map(...).filter...
由于这些函数使用一个迭代器并将它们映射到另一个迭代器,所以它们不会立即执行。但是当执行func3
时,partition.toList
会导致在func2
中映射闭包以执行。在分析时,似乎func3
花费了所有的时间,而func2
则具有code减慢应用程序的速度
(针对上述问题,
func2
包含一些将case对象序列化为json字符串的操作。它似乎只对每个线程上的第一个对象执行一些耗时的隐式代码。由于每个线程只执行一次,因此每个线程只有一个任务,需要很长时间,并解释了上面的事件时间线。)为什么在过滤器中发生物质化?与其他转换有何不同?另外,您能否提供一些关于DAG如何依赖于传递给mapPartitions的函数的更多信息?您好,我想说的是,在过滤器操作之后,我在第2点中详细说明了它。DAG通常是形式化的ed基于您拥有的三个函数,它检查哪些数据集被重用,或者在每个函数中如何重用,可能是第一个函数中的中间数据集可以在第二个和第三个函数中重用,根据DAG的形成。我试图模糊地解释它,但没有看到函数,很难解释如何导入orve执行。“它检查哪个数据集被重用”--因此,所有函数都是形式为(it:Iterator[SomeDomainObject])=>Iterator[AnotherDomainObject]
的闭包,并基于一些业务逻辑(涉及一些I/O和其他内容)对对象进行内部转换.在任何情况下,spark都不会注意到内部结构。我不明白:“…哪个数据集被重复使用…”:只有一个数据集,不是吗?是的,但是ds被分割到不同的分区,并被洗牌。这些分区上的操作由spark优化。嗨..是的,你是对的:ds被分割并洗牌。但是,在上面的沿袭中,在重新分割发生后,mapPartitions不需要洗牌(因此所有都是一个作业的一部分)。一旦在执行器上实现分区,spark将优化操作,但它不应涉及任何无序写入(除了写入计数,这是本例中的操作)。我发现了阻碍任务的瓶颈,它与spark无关。我将在回答中更新详细信息。(感谢您抽出时间!)