3 легких способа ускорить отдельные задачи Apache Spark

Автор Категория ,
3 легких способа ускорить отдельные задачи Apache Spark

Недавно мы рассматривали, как повысить производительность конвейеров Apache Spark и повысить скорость распределенных приложений для аналитики больших данных. Сегодня разберемся, почему тормозят отдельные Spark-задачи и как их ускорить. Читайте далее про инициализацию Спарк-контекста, предзагрузку артефактов и применение клиентского режима.

Почему некоторые задачи в быстром Apache Spark выполняются так медленно

Напомним, ключевым преимуществом Apache Spark для аналитической обработки Big Data с точки зрения разработчика распределенных приложений – это возможность писать код, не заботясь об особенностях распараллеливания, многопоточности, многопроцессорной обработки и других тонкостей работы в кластерных средах. Spark имеет довольно архитектуру, но простую модель разработки, позволяя эффективно использовать всю доступную вычислительную мощность кластера для корректно спроектированных программ. Организация распределенных вычислений происходит «под капотом» – фреймворк сам заботится о переводе кода в кластерный режим.

Однако, сократить операционные затраты на обработку Big Data, о чем мы писали здесь, необходимо понимать возможности оптимизации Spark-приложений, не полагаясь только на встроенные инструменты этого фреймворка. При этом стоит помнить, что Спарк оптимизирует пропускную способность обработки конвейера или блока данных, а не время обработки одиночного задания с минимальной задержкой. Из-за архитектурных особенностей фреймворка инициализация задач в нем происходит довольно медленно, до нескольких секунд. После запуска корректно спроектированные задания выполняются обычно быстро. Это еще раз подчеркивает, что изначально этот Big Data фреймворк был спроектирован именно для обработки больших объемов данных, когда время вычислений намного превышает время инициализации задачи.

Тем не менее, при построении Spark-конвейеров бывают случаи, когда запускается серия небольших задач. Поскольку такой вариант использования противоречит самой архитектуре фреймворка, не стоит ожидать от него высокой производительности. Однако, способы повышения скорости аналитических конвейеров работают и для небольших задач, снижая время их инициализации и выполнения [1]. Далее мы рассмотрим некоторые из них.

Ускорение инициализации

При создании задания Спарк создается SparkContext, в котором оно выполняется. Класс SparkContext является основной точкой входа в функциональные возможности фреймворка, представляет соединение с кластером, позволяет создавать в нем RDD, аккумуляторы и широковещательные переменные. Поскольку для каждой JVM активен только один SparkContext, перед созданием нового следует остановить текущий активный с помощью метода stop() [2].

Поскольку речь идет о JVM, активно потребляющей память, неудивительно, что инициализация контекста является весьма медленной. Поэтому для общего ускорения работы можно поддерживать SparkContext загруженным в памяти постоянно, например, с помощью Spark Job Server. Это отдельный сервис, который подключается к кластеру или встроенному автономному Спарк, управляет его заданиями и поддерживает создание постоянных контекстов, которые используются до тех пор, пока не будут явно удалены. Такой подход может снизить время начальной загрузки задачи на несколько секунд, т.к. во время работы Spark Job Server, предварительно инициализированный контекст многократно используется для выполнения заданий [1].

Предзагрузка кода и данных для вычислений

Для запуска приложений в кластере часто используется скрипт spark-submit из каталога bin. Он сам заботится о настройке пути к нужным классам и их зависимостям, поддерживает различные менеджеры кластеров и режимы развертывания [3]. Однако, если эти зависимости и другие артефакты весят довольно много, то отправка задачи замедляется. Решить эту проблему, выгадав несколько дополнительных секунд можно, сократив размер отправляемых данных. Для этого их следует разместить на каждом узле или внутри Docker-образа. Также иногда имеет смысл выполнять Spark-задание как клиентское приложение, а не в режиме кластера [1].

Выполнение постоянно загруженных в память Spark-заданий

Объединение двух вышеописанных подходов позволит еще более сократить время выполнения Spark-задания. Для этого приложение создается в режиме клиента и всегда запущено, а вычисления инициируются внешним событием. Таким образом, контекст является постоянным, а все модули фреймворка внутри JVM постоянно инициализированы и готовы к обработке данных. Также, чтобы сократить время инициализации и повысить производительность Спарк-приложений можно через конфигурирование настроек этого фреймворка [1]. В случае аналитики больших данных это особенно это актуально для соединений и группировки записей, которые относятся к так называемым операциям перемешивания (shuffle), когда по сети передается много данных. Об этом мы поговорим в другой раз. А о том, почему “тормозят” Спарк-приложения и как найти отстающую задачу, читайте в нашей новой статье.

На практике разобраться с особенностями инженерии больших данных и разработки распределенных Spark-приложений вы сможете на специализированных курсах в нашем лицензированном учебном центре обучения и повышения квалификации для разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве:

 

 

Источники

  1. https://bitworks.software/2019-04-15-how-to-run-low-latency-jobs-with-spark.html
  2. https://spark.apache.org/docs/latest/api/java/org/apache/spark/SparkContext.html
  3. https://spark.apache.org/docs/latest/submitting-applications.html