Scala 任务量大,程序最简单

Scala 任务量大,程序最简单,scala,apache-spark,apache-spark-sql,Scala,Apache Spark,Apache Spark Sql,我正在尝试用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 d

我正在尝试用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(_ + _))
    }
  }   
}
import org.apache.spark.{SparkContext,SparkConf}
对象LargeTaskTest{
def main(参数:数组[字符串]){
val conf=new SparkConf().setAppName(“数据测试”).setMaster(“本地[*]”)
val sc=新的SparkContext(配置)
val dat=(1到10000000)。toList
val data=sc.parallelize(dat).cache()

for(iReduce函数将所有数据发送到一个节点。当您运行
sc.parallelize
时,默认情况下,数据将分布到100个分区。要使用已分布的数据,请使用以下方法:

data.map(el=> el%100 -> el).reduceByKey(_+_)
或者您可以在分区级别执行reduce

data.mapPartitions(p => Iterator(p.reduce(_ + _))).reduce(_ + _)

或者只使用
sum
:)

因为您首先在本地创建非常大的列表,Spark
parallelize
方法尝试将此列表作为单个单元发送给Spark workers,作为任务的一部分。因此您会收到警告消息。作为替代方法,您可以将较小的列表并行化,然后使用
flatMap
将其分解为较大的列表列表。这还具有并行创建更大的数字集的好处。例如:

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()
}
编辑:

最终,要并行化的本地集合必须推送到执行器。方法创建一个ParallelCollectionRDD实例:

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创建的分区数量等于
numslice

  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
,在我的机器上是4。因此,即使在分割时,每个分区都包含一个非常大的列表,需要推送到执行器

SparkContext.parallelize
包含注释
@note parallelize动作迟缓
ParallelCollectionRDD
包含注释

//TODO:现在,每个拆分都会发送完整的数据,即使 稍后在RDD链中,它会被//缓存。这可能是值得的 将数据写入DFS中的文件并在拆分中读取// 相反


因此,调用reduce时似乎会出现问题,因为这是分区被发送到执行器的点,但根本原因是您在一个非常大的列表上调用parallelize。IMHO,在执行器中生成大列表是一种更好的方法。

正如我们从reduce函数中可以看到的那样,通过
runJob
其中定义了单个分区和合并步骤。因此它几乎不会将所有数据发送到单个NodeEyes。您是对的。reduce是完全可并行的。当您运行
sc.parallelize
时,驱动程序会将数据拆分为将发送到节点的分区。驱动程序所做的是确保每个进程都在运行g阶段发生在正确的时间(当数据可用时)。只有reduce函数的最后一个阶段将发送回驱动程序,其他所有操作都在节点级别进行。您是否尝试通过为默认参数
sc.parallelize提供值来增加分区数。请删除
。toList
at
val dat
定义。我认为源问题是s关闭内
dat
的erizlization发送给Master不是这样的。在并行化之后,每次迭代都会发生相同的警告。reduce导致警告,而不是并行化。您尝试过我的示例吗?它在并行化或reduce时都不会产生此类警告。因为您的dat对象可能低于任务内存警告限制,但我打赌它仍然会被发送给执行者,尽管它不应该被//TODO注释解释。Parallelize太懒了,每次操作都会发送它。谢谢。在调用
Parallelize
时,增加
numSlices
可以降低我从单个文件读取时的任务大小。