Повторное использование промежуточных результатов в нескольких вычислениях распространено во многих алгоритмах итеративного машинного обучения и графических алгоритмов, включая PageRank, кластеризацию K-средних и логистическую регрессию. В большинстве фреймворков единственный способ повторно использовать данные между вычислениями - это записать их во внешнюю стабильную систему хранения. Это влечет за собой значительные накладные расходы из-за репликации данных, дискового ввода-вывода и сериализации, которые могут доминировать во времени выполнения приложения. Хранение данных в памяти может повысить производительность на порядок.

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

RDD - это неизменяемый устойчивый распределенный набор записей. RDD - это основная абстракция данных в Apache Spark и ядро ​​Spark (которое многие часто называют Spark Core). Spark построен с использованием Scala на основе концепции RDD и предоставляет действия и преобразования поверх RDD. RDD распределены по дизайну, и для достижения равномерного распределения данных, а также использования локальности данных они разделены на фиксированное количество разделов - логических фрагментов (частей) данных.

Приложение Spark состоит из единственной программы драйвера, которая запускает main функцию пользователя, и набора программ исполнителя, разбросанных по узлам кластера. Драйвер определяет один или несколько RDD и вызывает над ними действия. Рабочие (также известные как подчиненные) запускают экземпляры Spark, в которых исполнители живут для выполнения задач.

Преобразования - это функции, которые принимают RDD в качестве входных данных и создают один или несколько RDD в качестве выходных. Применяя преобразования, вы постепенно создаете род RDD со всеми родительскими RDD окончательного (ых) RDD (ов). Это мощное свойство: по сути, делает RDD отказоустойчивым (отказоустойчивым). Если раздел RDD потерян, RDD имеет достаточно информации о том, как он был получен из других RDD, чтобы пересчитать только этот раздел. Логический план, то есть DAG, материализуется и выполняется, когда SparkContext запрашивается для запуска задания Spark. Некоторые преобразования могут быть конвейерными, что является оптимизацией, которую Spark использует для повышения производительности вычислений.

Действия - это операции RDD, которые производят значения, не относящиеся к RDD. Они воплощают ценность в программе Spark. Проще говоря, действие оценивает граф происхождения RDD. Действия - это один из двух способов отправки данных от исполнителей к драйверу (другой - накопители).

Spark управляет данными с помощью разделов, что помогает распараллеливать распределенную обработку данных с минимальным сетевым трафиком для отправки данных между исполнителями. RDD разделяются автоматически без вмешательства программиста. Однако бывают случаи, когда вы хотите отрегулировать размер и количество разделов или схему разделения в соответствии с потребностями вашего приложения.

Откройте оболочку Spark и выполните задание Spark. Любое действие преобразуется в задание, которое, в свою очередь, снова делится на этапы, причем каждый этап имеет свой собственный набор задач.

sc.parallelize(1 to 100).count

Когда этап выполняется, вы можете увидеть количество разделов для данного этапа в пользовательском интерфейсе Spark.

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

sc.parallelize(1 to 100, 2).count

RDD содержит ссылку на свой массив разделов, который вы можете использовать, чтобы узнать, сколько разделов существует.

val randRDD = sc.parallelize(1 to 100, 30)
randRDD.partitions.size

Spark необходимо запустить по одной задаче на каждый раздел RDD. Лучше всего, чтобы каждая задача, отправляемая на машину, имела раздел, который эта задача должна обрабатывать. В этом случае задача сможет прочитать данные раздела с локальной машины. В противном случае задача должна была бы получить данные раздела по сети с другого компьютера, что менее эффективно.