Почему так много задач в моей искровой работе?

У меня есть искровое задание, которое берет файл с 8 записями из hdfs, делает простой агрегат и сохраняет его обратно в Hadoop. Я замечаю, что, когда я это делаю, есть сотни задач.

Я также не уверен, почему для этого есть несколько заданий? Я думал, что работа больше похожа, когда произошло действие. Я могу рассказать о том, почему, но я понял, что внутри этого кода это должно быть одно задание, и оно должно быть разбито на этапы, а не на несколько заданий. Почему он просто не разбивает его на этапы, как он перерывы на работу?

Что касается задач с 200 плюс, поскольку объем данных и количество узлов минимальны, не имеет смысла, что для каждой строки данных существует как 25 задач, когда есть только одна совокупность и пара фильтров. Почему у него просто одна задача для каждого раздела на атомную операцию?

Вот соответствующий код scala -

import org.apache.spark.sql._
import org.apache.spark.sql.types._
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf

object TestProj {object TestProj {
  def main(args: Array[String]) {

    /* set the application name in the SparkConf object */
    val appConf = new SparkConf().setAppName("Test Proj")

    /* env settings that I don't need to set in REPL*/
    val sc = new SparkContext(appConf)
    val sqlContext = new SQLContext(sc)
    import sqlContext.implicits._

    val rdd1 = sc.textFile("hdfs://node002:8020/flat_files/miscellaneous/ex.txt")

     /*the below rdd will have schema defined in Record class*/
     val rddCase =  sc.textFile("hdfs://node002:8020/flat_files/miscellaneous/ex.txt")
                      .map(x=>x.split(" "))    //file record into array of strings based spaces
                      .map(x=>Record(
                                     x(0).toInt,
                                     x(1).asInstanceOf[String],
                                     x(2).asInstanceOf[String],
                                     x(3).toInt
                                    ))


    /* the below dataframe groups on first letter of first name and counts it*/
    val aggDF = rddCase.toDF()
                       .groupBy($"firstName".substr(1,1).alias("firstLetter"))
                       .count
                       .orderBy($"firstLetter")

    /* save to hdfs*/
    aggDF.write.format("parquet").mode("append").save("/raw/miscellaneous/ex_out_agg")

  }

    case class Record(id: Int
                    , firstName: String
                    , lastName: String
                    , quantity:Int)

}

Ниже показан снимок экрана после нажатия на приложение введите описание изображения здесь

Ниже показаны этапы, показывающие при просмотре конкретного "задания" id 0 введите описание изображения здесь

Ниже приведена первая часть экрана при нажатии на сцену с более чем 200 заданиями.

введите описание изображения здесь

Это вторая часть экрана внутри сцены введите описание изображения здесь

Ниже после нажатия на вкладку "Исполнители" введите описание изображения здесь

В соответствии с запросом, вот этапы для Идентификатора задания 1

введите описание изображения здесь

Вот подробности для этапа в ID задания 1 с 200 заданиями

введите описание изображения здесь

Ответ 1

Это классический вопрос Spark.

Две задачи, используемые для чтения (Stage Id 0 на втором рисунке), - это параметр defaultMinPartitions, который установлен на 2. Вы можете получить этот параметр, прочитав значение в REPL sc.defaultMinPartitions. Он также должен быть видимым в интерфейсе Spark под краном "Окружающая среда".

Вы можете посмотреть код из github, чтобы убедиться, что это именно то, что происходит. Если вы хотите, чтобы при чтении читалось больше разделов, просто добавьте его как параметр, например, sc.textFile("a.txt", 20).

Теперь интересная часть происходит из 200 разделов, которые выходят на второй этап (Stage Id 1 на втором рисунке). Ну, каждый раз, когда происходит перетасовка, Спарк должен решить, сколько разделов будет иметь случайное воспроизведение RDD. Как вы можете себе представить, по умолчанию 200.

Вы можете изменить это, используя:

sqlContext.setConf("spark.sql.shuffle.partitions", "4")

Если вы запустите свой код с этой конфигурацией, вы увидите, что 200 разделов больше не будут там. Как установить этот параметр является своего рода искусством. Возможно, выберете 2x количество ядер, которые у вас есть (или что-то еще).

Я думаю, что Spark 2.0 имеет возможность автоматически выводить максимальное количество разделов для перетасовки RDD. С нетерпением ждем этого!

Наконец, количество заданий, которые вы получаете, связано с тем, сколько из RDD-действий получило результат, полученный в результате оптимизированного кода Dataframe. Если вы читаете спецификации Spark, то говорится, что каждое действие RDD вызывает одно задание. Когда вы выполняете действия с использованием Dataframe или SparkSQL, оптимизатор Catalyst будет определять план выполнения и генерировать некоторый код на основе RDD для его выполнения. Трудно точно сказать, почему в вашем случае используется два действия. Возможно, вам стоит взглянуть на оптимизированный план запросов, чтобы увидеть, что именно происходит.

Ответ 2

У меня такая же проблема. Но в моем сценарии коллекция, которую я распараллеливаю, имеет меньше элементов, чем количество заданий, запланированных Spark (что приводит к тому, что искра иногда ведет себя странно). Используя номер принудительного номера, я смог исправить эту проблему.

Это было что-то вроде этого:

collection = range(10) # In the real scenario it was a complex collection
sc.parallelize(collection).map(lambda e: e + 1) # also a more complex operation in the real scenario

Затем я увидел в журнале Spark:

INFO YarnClusterScheduler: Adding task set 0.0 with 512 tasks