我正在尝试使用Spark
运行最简单的程序import org.apache.spark.{SparkContext, SparkConf}
object LargeTaskTest {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("DataTest").setMaster("local[*]")
val sc = new SparkContext(conf)
val dat = (1 to 10000000).toList
val data = sc.parallelize(dat).cache()
for(i <- 1 to 100){
println(data.reduce(_ + _))
}
}
}
每次迭代后我都会收到以下错误消息:
WARN TaskSetManager:阶段0包含一个非常大的任务(9767 KB)。建议的最大任务大小为100 KB。
增加数据大小会增加所述任务大小。这告诉我,驱动程序正在向所有执行程序发送“dat”对象,但我不能为我的生活看到原因,因为我的RDD上的唯一操作是reduce,它基本上没有关闭。有任何想法吗 ?
答案 0 :(得分:5)
因为您首先在本地创建非常大的列表,所以Spark flatMap
方法尝试将此列表作为单个单元发送给Spark工作器,作为任务的一部分。因此,您收到警告信息。作为替代方案,您可以并行化一个小得多的列表,然后使用import org.apache.spark.{SparkContext, SparkConf}
object LargeTaskTest extends App {
val conf = new SparkConf().setAppName("DataTest").setMaster("local[*]")
val sc = new SparkContext(conf)
val dat = (0 to 99).toList
val data = sc.parallelize(dat).cache().flatMap(i => (1 to 1000000).map(j => j * 100 + i))
println(data.count()) //100000000
println(data.reduce(_ + _))
sc.stop()
}
将其分解为更大的列表。这也有利于并行创建更大的数字集。例如:
parallelize
编辑:
最终,并行化的本地集合必须被推送到执行程序。 def parallelize[T: ClassTag](
seq: Seq[T],
numSlices: Int = defaultParallelism): RDD[T] = withScope {
assertNotStopped()
new ParallelCollectionRDD[T](this, seq, numSlices, Map[Int, Seq[String]]())
}
方法创建ParallelCollectionRDD的实例:
numSlices
ParallelCollectionRDD创建了许多等于 override def getPartitions: Array[Partition] = {
val slices = ParallelCollectionRDD.slice(data, numSlices).toArray
slices.indices.map(i => new ParallelCollectionPartition(id, i, slices(i))).toArray
}
的分区:
numSlices
sc.defaultParallelism
默认为SparkContext.parallelize
,在我的机器上是4.所以即使拆分,每个分区都包含一个非常大的列表,需要将其推送到执行程序。
@note Parallelize acts lazily
包含注释ParallelCollectionRDD
,<option>
包含注释;
// TODO:现在,每个分组都会发送完整的数据,即使是 后来在它被缓存的RDD链中。这可能是值得的 将数据写入DFS中的文件并在拆分中读取// 代替。
因此,当您调用reduce时,问题就会发生,因为这是将分区发送给执行程序的要点,但根本原因是您在一个非常大的列表上调用并行化。在执行器中生成大型列表是一种更好的方法,恕我直言。
答案 1 :(得分:-2)
Reduce函数将所有数据发送到一个节点。运行sc.parallelize
时,数据默认分配给100个分区。要使用已经分布的数据,请使用以下内容:
data.map(el=> el%100 -> el).reduceByKey(_+_)
或者您可以在分区级别执行reduce。
data.mapPartitions(p => Iterator(p.reduce(_ + _))).reduce(_ + _)
或只使用sum
:)