Apache Spark 4.0.0: Главные изменения, которые стоит знать

Первый сентябрьский день — время не только закупаться тетрадками и карандашами, но и отличная пора для обновления технологического стека. Тем более что не так давно вышел долгожданный Apache Spark 4.0 — первый мажорный релиз за последние несколько лет, который принёс столько изменений, что хватит на целый учебный год.

Вместо того, чтобы читать километры release notes, давайте сразу перейдём к самому важному: что нового появилось, что сломалось и как все эти изменения повлияют на данные и наши пайплайны.

Что требует внимания в первую очередь?

Сразу же рассмотрим изменения, которые ломают обратную совместимость. Их никак нельзя игнорировать при планировании перехода на Spark 4.0.

  • Scala 2.12 официально похоронен — теперь как минимум Scala 2.13. Это заставляет наконец-то мигрировать всех, кто застрял на старой версии.

  • Поддержка Java 8 и 11 также прекращена — обязательная версия теперь — это Java 17. Это огромнейший шаг, открывающий доступ к новым возможностям языка и лучшей производительности JVM. Здесь стоит отметить, что если кто-то до сих пор пользуется Spark с Java 8, то стоит постепенно обновить версию Java сначала до 11, а уже после до 17 (Spark с версии 3.5 её также уже поддерживает и этот шаг существенно упростит дальнейшую миграцию на Spark 4.0).

  • Включен строгий ANSI-режим по умолчанию — опция spark.sql.ansi.enabled теперь по умолчанию установлена в значение true. Таким образом, пайплайны, которые раньше молча прощали арифметические переполнения или неверные операции с датами, теперь будут падать с ошибками. Это значительное изменение в сторону корректности данных. Вернуть предыдущее поведение можно, установив значение опции spark.sql.ansi.enabled или переменной SPARK_ANSI_SQL_MODE в false.

  • Удалена поддержка Mesos — если кто-то всё ещё использует Mesos, то пора наконец-то задуматься о миграции на Kubernetes или YARN.

Подготовка к переходу на Spark 4.0 — это не просто обновление библиотек. Это полноценный проект по миграции с обновлением стека языков, рантаймов и, возможно, даже самого кластера.

Spark Connect — наше будущее

Spark Connect, который анонсировали в Spark 3.4, в версии 4.0 переживает взрывное развитие и практически становится центральной архитектурой для клиентских приложений.

  • Новый легковесный Python-клиентpyspark-client весит всего 1.5 Мб против сотен мегабайт стандартного PySpark.

  • Полная поддержка Java-клиента — теперь не только Python, но и Java-приложения смогут использовать тонкий клиент для подключения к удалённому Spark-кластеру.

  • ML на Spark Connect — появилась возможность тренировать ML-модели через удалённое соединение, что раньше было ограничено.

  • Swift-клиент — появление клиента для языка Swift — это довольно интересный шаг в сторону использования Spark в мобильной разработке.

Архитектура Spark окончательно разделяется на производительный, но тяжёлый движок на кластере и легковесные клиенты для доступа к нему. Это существенно меняет принципы разработки и деплоя приложений.

Spark SQL — мощь и выразительность

  • Тип данных VARIANT (возможно, главная фича всего релиза для SQL) — предназначен для обработки полуструктурированных данных, например, JSON, позволяя хранить иерархические структуры данных в одном столбце и эффективно работать с ними. Также поддерживает работу с данными без предварительного определения схемы.
    Однако в настоящее время у него есть некоторые ограничения:
    • не все операции поддерживаются, например, выбор значений по ключу пока в разработке
    • такое поле не может быть использовано для группировки, сортировки или выступать в качестве ключа для партиционирования
    • также не будет работать операция distict
  • SQL User-Defined Functions (UDFs) — теперь можно создавать пользовательские функции прямо на SQL, без погружения в Scala или Python. Это будет огромный плюс для наших аналитиков.

    CREATE FUNCTION square(x INT) RETURNS INT RETURN x * x;
    SELECT square(5); -- 25
    

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

  • Session Variables — аналог переменных в традиционных СУБД. Крайне полезно для передачи параметров между несколькими запросами в рамках одной сессии.

    -- Устанавливаем переменные сессии в начале скрипта или ноутбука
    SET report_date = '2025-09-01';
    SET country = 'Russia';
      
    -- Используем их в первом запросе
    SELECT COUNT(*) AS total_orders
    FROM orders
    WHERE date = ${report_date} AND country = ${country};
      
    -- Используем те же переменные во втором запросе
    SELECT SUM(revenue) AS total_revenue
    FROM sales
    WHERE date = ${report_date} AND country = ${country};
    
    -- И в третьем, четвертом, пятом...
    -- Чтобы изменить дату и страну для всего отчета, 
    -- нужно поменять значения всего в двух командах SET.
    

    Кроме того в переменную можно записать и результат подзапроса:

    -- Устанавливаем порог (количество сигм)
    SET sigma_threshold = 2.0;
      
    -- Считаем среднее и стандартное отклонение
    SET avg_revenue = (SELECT AVG(revenue) FROM sales);
    SET stddev_revenue = (SELECT STDDEV(revenue) FROM sales);
      
    -- Рассчитываем границу и используем сразу несколько переменных
    SET revenue_threshold = ${avg_revenue} + (${sigma_threshold} * ${stddev_revenue});
      
    -- Фильтруем выбросы по рассчитанному порогу
    SELECT *
    FROM sales
    WHERE revenue > ${revenue_threshold};
    
  • Поддержка колляций (String Collation) — теперь правильная сортировка строк с учетом кодировки, языка и регистра наконец-то встроена в движок. Также если правильно указать кодировку, можно существенно увеличить производительность при сравнении и фильтрации строк.

  • Встроенная поддержка XML — добавлена к стандартным JSON, CSV, Avro. Парсинг XML с использованием новых функций from_xml, schema_of_xml и xpath становится тривиальной задачей.

  • Pipe-синтаксис — теперь можно организовать цепочки преобразований в SQL в более удобном, а главное более читабельном, последовательном стиле.

    Вместо, например, такого стандартного запроса:

    SELECT c_count, COUNT(*) AS custdist FROM
      (SELECT c_custkey, COUNT(o_orderkey) c_count FROM customer
      LEFT OUTER JOIN orders ON c_custkey = o_custkey
      AND o_comment NOT LIKE '%unusual%packages%' GROUP BY c_custkey) AS c_orders
    GROUP BY c_count
    ORDER BY custdist DESC, c_count DESC;
    

    Можно писать следующим образом:

    FROM customer
     |> LEFT OUTER JOIN orders ON c_custkey = o_custkey
        AND o_comment NOT LIKE '%unusual%packages%'
     |> AGGREGATE COUNT(o_orderkey) c_count
        GROUP BY c_custkey
     |> AGGREGATE COUNT(*) AS custdist
        GROUP BY c_count
     |> ORDER BY custdist DESC, c_count DESC;
    

    Подробнее о синтаксисе можно узнать на странице исследования в Google Research.

Spark SQL и DataFrame/Dataset API получили огромное количество новых функций, которые закрывают давние боли и открывают множество новых сценариев, что продолжает превращать Spark SQL в полнофункциональную распределённую СУБД.

PySpark растёт и развивается

  • Python Data Source API V2 — это переписанный и более эффективный API для написания коннекторов к данным (в том числе и собственных) на чистом Python.

  • User-Defined Table Functions (UDTFs) — мощнейшая возможность для пользовательских функций, позволяющая возвращать из Python-функций целые таблицы, а не отдельные значения или строки. Наконец-то это работает не только для Scala или Java.

  • Plotting API — добавлен нативный API для построения графиков прямо из Spark DataFrame, без необходимости вытягивания данных в Pandas и использования дополнительных библиотек. В результате легковесный Python-клиент для Spark Connect теперь может не только выполнять запросы, но и строить графики, что открывает широкие возможности для удалённой аналитики.

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

Structured Streaming

  • Arbitrary State API v2 — новая, более гибкая версия API для работы с состоянием в стриминге. Она даёт гораздо больший контроль и лучшую производительность. Одна из тех фич Spark, для которой стоит потратить время на написание отдельной статьи.

  • State Data Source — пожалуй, главнейшая фича релиза для стриминга! Позволяет нам запросить состояние стримингового приложения напрямую, как из обычной таблицы. Это решает огромную проблему отладки и мониторинга состояния. И это также достойно своей статьи в блоге.

  • Python Streaming Data Sources — теперь создание собственных источников для чтения потоковых данных на Python стало реальностью.

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

Мониторинг и управление

  • Structured Logging — логи перестают быть просто текстом, а становятся структурированными данными, которые не только проще парсить, но и возможно легко обрабатывать, агрегировать и анализировать в том числе и самим Spark’ом.

  • Spark Kubernetes Operator — реализован отдельный оператор, который упрощает управление жизненным циклом Spark-приложений в K8s.

  • Улучшенный Spark UI

    • флейм-графы для дампов потоков
    • обновлённая отрисовка для DAG’ов
    • метрики для Python UDF
    • и другие мелочи…

Итоги

Apache Spark 4.0 — это не просто обновление. Это полноценная смена эпохи в обработке данных с помощью Spark. Как сам Spark и его библиотеки получили множество изменений, нововведений и улучшений, так и вся его экосистема и зависимости были обновлены до актуальных версий, а всё устаревшее отрезано и забыто.

Релиз получился насыщенным, сложным и с большим заделом на будущее. Что же по поводу рекомендаций:

  • Для новых проектов — смело начинайте с Spark 4.0.0, учитывая требования к Java 17 и Scala. Это позволит уже сейчас получить массу новых возможностей и буст по производительности.

  • Для существующих проектов — планируйте миграцию как большой и сложный проект. Если у вас версия Spark ниже 3.5, то начните с обновления до неё, после чего стоит обновить Java и Scala. Затем протестируйте все пайплайны с включенной опцией spark.sql.ansi.enabled=true. И только после решения всех возникших при этом проблем обновляйте сам Spark до версии 4.

Apache Spark 4.0 — это наше будущее, которое наступает уже сегодня.