Привет, Хабр! Я Станислав Габдулгазиев, архитектор департамента поддержки продаж Arenadata. Кажется, ещё вчера мы радовались возможностям Apache Spark 3.0, разбирались с Adaptive Query Execution и наслаждались улучшениями Pandas API. Но мир больших данных не стоит на месте, и вот уже на подходе Apache Spark 4.0. Новый мажорный релиз — это всегда событие: он обещает новые фичи, прирост производительности и, конечно же, новые вызовы при миграции.

Apache Spark де-факто стал стандартом для распределённой обработки данных. От классических ETL-пайплайнов и SQL-аналитики до сложного машинного обучения и стриминга — Spark так или иначе задействован во многих современных data-платформах. Поэтому каждый новый релиз вызывает живой интерес у комьюнити: что там под капотом? Какие проблемы решены? Не сломается ли то, что работало годами?

В этой статье попробуем разобраться в ключевых различиях между Spark 3.0 и 4.0. Мы погрузимся в новые возможности, обсудим ожидаемый прирост производительности, затронем неизбежные изменения в API и потенциальные трудности перехода. Наша цель — дать вам достаточно информации, чтобы вы могли принять взвешенное решение: планировать апгрейд на Spark 4.0 или пока оставаться на стабильной и знакомой версии 3.0.

Spark 3.0 — краткий обзор «старой школы»

Прежде чем нырять в глубины Spark 4.0, давайте быстро пробежимся по ключевым моментам, которые сделали Spark 3.0 таким значительным релизом и рабочей лошадкой для многих команд. Вышедший в июне 2020 года, Spark 3.0 и его последующие минорные обновления (3.1–3.5) принесли массу улучшений, закрепив за ним репутацию стабильной и производительной версии.

Вот некоторые из главных фишек, которые появились в линейке 3.0:

1. Adaptive Query Execution (AQE). Пожалуй, одна из самых громких фич. AQE позволяет Spark оптимизировать планы выполнения запросов прямо во время их работы, адаптируясь к реальным размерам данных после шаффлов. Это включает динамическое изменение числа редьюсеров, автоматическое преобразование sort-merge join в broadcast join и оптимизацию обработки скошенных данных (skew join optimization). Для многих запросов это дало заметный буст производительности «из коробки».

2. Dynamic Partition Pruning (DPP). Серьёзная оптимизация для запросов к партицированным таблицам, особенно в сценариях «звезда» (star schema). DPP позволяет использовать результаты одной стороны join (обычно dimension-таблицы) для фильтрации партиций на другой стороне (fact-таблицы) ещё на этапе сканирования, значительно сокращая объём читаемых данных.

3. Улучшения ANSI SQL Compliance. Spark 3.0 сделал большой шаг к увеличению совместимости со стандартом ANSI SQL, что упростило миграцию SQL-запросов с других систем и сделало поведение Spark более предсказуемым для аналитиков.

4. Переработанный Pandas API (API on Spark). Изначально известный как Koalas, этот API был интегрирован в PySpark, чтобы предоставить дата-сайентистам привычный pandas-like-интерфейс для работы с большими данными на spark-кластере. Это существенно снизило порог входа для тех, кто привык к экосистеме Python.

5. Поддержка бинарных файлов. Новый data source binaryFile позволил эффективно читать и преобразовывать файлы (например, изображения) в DataFrame, открыв двери для новых ML/AI-сценариев.

6. Accelerator-Aware Scheduling. Улучшенная поддержка GPU и других ускорителей, что важно для ускорения ML-ворклоадов.

7. Множество улучшений в Structured Streaming, MLlib и коннекторах. Среди них — более гибкая работа с состоянием потоков, новые алгоритмы машинного обучения, улучшения поддержки GPU и расширение DataSource V2 API для работы с транзакционными хранилищами.

Spark 3.0 не просто добавил новые функции, но и сосредоточился на стабильности, производительности и удобстве использования, став зрелой платформой для самых разных задач обработки данных. Именно на этом прочном фундаменте и строится Spark 4.0.

Spark 4.0 — что нового под капотом?

Итак, чем же Spark 4.0 отличается от своего предшественника? Разработчики явно сфокусировались на нескольких ключевых направлениях: улучшениях для Python-разработчиков, повышении строгости и совместимости SQL, модернизации основной платформы (новые версии Java/Scala) и, конечно, новых фичах для производительности и расширения функциональности. Давайте по порядку.

1. Python наносит ответный удар (PySpark Enhancements):

Python-комьюнити может ликовать. Spark 4.0 делает серьёзную ставку на PySpark, делая его ещё мощнее и удобнее:

  • Python User Defined Table Functions (UDTFs). Эта киллер-фича, впервые представленная в экспериментальном виде в Spark 3.5, в Spark 4.0 получает полноценную реализацию и стабильность. Теперь вы можете писать пользовательские функции, которые возвращают целые таблицы, а не только скалярные значения или строки. Добавление полиморфных Python UDTF в 4.0 придаёт ещё больше гибкости. Этот функционал открывает новые горизонты для сложной обработки данных и бесшовной интеграции с вашими любимыми python-библиотеками прямо внутри spark-SQL-запросов.

  • Arrow-оптимизированные Python UDFs. Продолжается работа над ускорением UDF через Apache Arrow. Ожидается дальнейшее снижение оверхеда на сериализацию/десериализацию данных между JVM и Python.

  • Python Data Source API. Позволяет разработчикам реализовывать коннекторы к источникам данных полностью на Python. Раньше это было прерогативой Scala/Java, теперь экосистему PySpark можно расширять гораздо проще.

  • Поддержка Pandas 2.x и PyArrow 11+. PySpark теперь требует более свежие версии библиотек, включая Pandas ≥ 2.0.0 и PyArrow ≥ 11.0.0. Это позволяет использовать последние фичи этих библиотек, но также означает breaking changes! Прощайте, iteritems, append, mad и некоторые параметры в Pandas API on Spark — придётся рефакторить код (подробнее в разделе про миграцию).

  • Отказ от Python 3.7 (и, возможно, 3.8). Минимально поддерживаемая версия Python поднимается (скорее всего, до 3.9+), так что пора обновлять окружения.

  • Унифицированное профилирование UDF. Обещают улучшенные инструменты для анализа производительности Python UDF.

2. SQL становится «умнее» (Core & SQL Engine):

  • Spark Connect. Хотя Spark Connect был представлен до релиза 4.0, его развитие и интеграция тесно связаны с новой версией и являются одним из ключевых изменений, меняющих парадигму взаимодействия. Spark Connect предоставляет decoupled thin client для подключения к spark-кластеру из любого места, где можно запустить клиентское приложение (IDE, ноутбуки, сервисы). Это значительно упрощает разработку на разных языках, позволяя выполнять операции Spark удалённо, без необходимости разворачивать JVM-процессы локально. Фактически это шаг к превращению Spark в полноценную data lakehouse platform с единой точкой доступа. Его усиленное развитие в контексте 4.0 подчёркивает его роль как центрального элемента будущих архитектур.

  • Развитие DataSource V2 API. Одним из ключевых, хотя и менее заметных, «под капотом», изменений является постоянное совершенствование API-источников данных версии 2 (DataSource V2). Этот API является основой для реализации таких возможностей, как эффективная работа с транзакционными хранилищами (например, Delta Lake, Apache Iceberg), push-down-операций к источнику данных и, что важно, более гранулярных row-level-операций (UPDATE, DELETE, MERGE) напрямую через Spark SQL. В Spark 4.0 этот API продолжает развиваться, становясь более стабильным и функциональным, что критично для построения современных data-lakehouse-решений.

  • ANSI SQL режим по умолчанию. Одно из самых значительных изменений! Spark 4.0 будет работать в режиме ANSI SQL из коробки. Это значит более строгое следование стандарту SQL, явные ошибки вместо null в некоторых операциях (например, при переполнении типов), улучшенная переносимость SQL-кода. Потребует внимания при миграции, но в долгосрочной перспективе повысит надёжность и предсказуемость запросов.

  • VARIANT Data Type. Новый тип данных для эффективной работы с полуструктурированными данными (JSON, Avro и т. д.) внутри Spark. Вместо хранения JSON как строки, VARIANT позволяет парсить его один раз и затем эффективно запрашивать вложенные поля, обещая прирост производительности и лучшее сжатие.

  • Поддержка Collation (сопоставления строк). Позволяет корректно сортировать и сравнивать строки с учётом языковых особенностей (регистр, диакритические знаки). Очень важно для мультиязычных приложений и совместимости с традиционными БД.

  • Улучшения Catalyst Optimizer и AQE. «Под капотом» продолжают докручивать оптимизатор запросов и адаптивное выполнение. Ожидаются дальнейшие улучшения в переупорядочивании join’ов, DPP и других техниках.

  • Структурированное логирование и улучшенные ошибки. Вводится фреймворк для структурированных логов (проще парсить и анализировать) и стандартизированные классы ошибок. Дебаг и мониторинг должны стать приятнее.

  • Identity Columns. Поддержка автоинкрементных колонок / колонок с генерируемыми значениями, как в классических СУБД.

3. Модернизация платформы:

  • Scala 2.13. Хотя частичная поддержка Scala 2.13 появилась ещё в линейке Spark 3.x, Spark 4.0 полностью переходит на Scala 2.13. Это долгожданное обновление приносит улучшения в самой Scala, особенно в работе с коллекциями, и позволяет использовать более современные scala-библиотеки. Этот переход также подчёркивает общую стратегию проекта по эволюции платформы, постепенно сокращая привязку к специфическим версиям Scala и развивая API, доступные из разных языков. Важно учитывать, что проекты, разработанные на Scala 2.12, потребуют перекомпиляции и возможной адаптации кода при миграции на 4.0.

  • Java 17 по умолчанию, поддержка Java 21. Spark теперь будет собираться и работать на Java 17 по дефолту (вместо Java 11 в 3.x), а также добавлена поддержка Java 21. Это позволяет использовать новые возможности JVM и потенциально получить прирост производительности.

4. Улучшения в стриминге (Structured Streaming):

  • Arbitrary Stateful Processing v. 2. Эволюция API для работы с состоянием в потоковых задачах. Больше гибкости для сложных сценариев: поддержка композитных типов в GroupState, управление вытеснением состояния (state eviction), эволюция схемы состояния.

  • State Reader API Enhancements. Появились возможности для чтения «фида изменений» (change feed) из state store в стандартном CDC-формате и создания снепшотов состояния на определённый момент времени. Это сильно упрощает отладку, мониторинг и анализ состояния стриминговых приложений.

  • Streaming State Store как Data Source. Возможность напрямую читать state store как обычный источник данных Spark.

5. MLlib

  • MLlib. Получает обновления с новыми алгоритмами и улучшениями существующих, а также выигрывает от общих улучшений производительности ядра Spark и лучшей интеграции с ускорителями.

Как видите, изменений в Spark 4.0 хватает. Некоторые из них — эволюционные улучшения, другие — довольно революционные (вроде ANSI по умолчанию или Scala 2.13). В следующих разделах посмотрим, как это всё сказывается на производительности и что нужно учесть при миграции.

Бенчмарки и производительность: 3.0 vs 4.0

Сразу оговоримся: на момент выхода нового мажорного релиза найти исчерпывающие, независимые бенчмарки (вроде TPC-DS), сравнивающие его «в лоб» с предыдущей стабильной версией, бывает непросто. Команды и энтузиасты только начинают гонять тесты на реальных и синтетических ворклоадах. Поэтому пока мы больше будем опираться на ожидаемый прирост производительности, вытекающий из новых фич, и на первые отчёты и заявления разработчиков или крупных пользователей (вроде Databricks или AWS, которые часто публикуют свои тесты).

Где же мы можем ожидать ускорения в Spark 4.0?

1. PySpark-ворклоады. Это, пожалуй, главный кандидат на заметный прирост. Исторически Python UDF (особенно невекторизованные) были узким местом из-за оверхеда на передачу данных и переключение контекста между JVM и Python. Усилия по оптимизации через Apache Arrow, внедрение UDTF и общие улучшения в PySpark должны сократить этот разрыв. Если ваши пайплайны активно используют python-код, переход на 4.0 потенциально может принести ощутимый выигрыш. Но помните золотое правило: нативные функции Spark SQL > векторизованные UDF (Pandas/Arrow) > обычные Python UDF. Если можете переписать логику на нативные функции — это почти всегда будет быстрее.

2. Работа с полуструктурированными данными (JSON/Avro). Новый тип VARIANT выглядит очень многообещающе. Хранение JSON как строки заставляет Spark парсить её при каждом обращении. VARIANT собирает данные один раз при чтении и хранит их в оптимизированном бинарном формате. Это должно значительно ускорить запросы к полям внутри JSON-документов, особенно если у вас глубокая вложенность или частые запросы к разным полям. В некоторых источниках упоминается и потенциально лучшее сжатие.

3. SQL и Core Engine. Здесь улучшения могут быть не такими драматичными, как в PySpark, но они есть. Дальнейшее развитие Adaptive Query Execution (AQE) и оптимизатора Catalyst может дать прирост на сложных SQL-запросах «из коробки». Переход на Java 17/21 и Scala 2.13 также может дать небольшой общий прирост за счёт улучшений в самой JVM и стандартных библиотеках Scala.

4. Stateful Streaming. Новый API transformWithState для работы с состоянием в Structured Streaming не только упрощает код, но и, судя по анонсам, оптимизирован для лучшей производительности и эффективности управления состоянием (например, за счёт нативной поддержки TTL, композитных типов).

5. Machine Learning. Появляются отчёты об улучшениях в ML-сценариях. Например, упоминается ускорение распределённого обучения (благодаря лучшей интеграции с Horovod/PyTorch Lightning) и заметный прирост (до 30% по некоторым данным) для библиотек вроде LightGBM на Spark 4.0 по сравнению с 3.x, этому даёт возможность улучшенное управление ресурсами и оптимизации ядра.

Важное НО:

Всегда помните про YMMV (Your Mileage May Vary) — ваш реальный опыт может отличаться от приведённых оценок и заявлений, особенно в зависимости от специфики задач и конфигурации кластера. Прирост производительности сильно зависит:

  • От вашего конкретного ворклоада. Какие операции преобладают? SQL, ETL, ML, Streaming? Насколько интенсивно используется Python?

  • Характера данных. Объёмы, форматы, распределение ключей (skewness).

  • Конфигурации кластера и Spark. Настройки памяти, параллелизма, включённые/выключенные фичи (тот же AQE).

  • Железа. CPU, память, диски, сеть.

Вывод по производительности: Spark 4.0 несёт в себе ряд архитектурных изменений и оптимизаций, которые потенциально могут дать заметный прирост производительности, особенно в python- и JSON-интенсивных задачах, а также в ML и Stateful Streaming. Однако не стоит слепо верить маркетинговым заявлениям. Единственный надёжный способ узнать, получите ли вы профит, — это провести собственные бенчмарки на ваших реальных данных и задачах после выхода стабильного релиза 4.0.

Депрекейты и удалённые фичи. О чём придётся забыть?

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

1. Прощай, старая гвардия:

  • Scala 2.12. Поддержка Scala 2.12 полностью прекращена. Spark 4.0 собран и работает только с Scala 2.13. Это значит, что все ваши scala-проекты для Spark придётся перекомпилировать под Scala 2.13, что может потребовать адаптации кода из-за изменений в самой Scala.

  • Hive Metastore. Удалена поддержка версий Hive Metastore старше 2.0.0 (из-за их зависимости от Java 8).

  • Java 8 / Java 11. Spark 4.0 требует как минимум Java 17 для работы (и поддерживает Java 21). Поддержка Java 8 и, скорее всего, Java 11 прекращена. Пора обновлять JDK на ваших машинах и в CI/CD-пайплайнах.

  • Python 3.8. Поддержка Python 3.8 удалена. Минимально необходимой версией, вероятно, станет Python 3.9.

  • Старые версии py-библиотек. Минимальные требуемые версии подняты:

    • для Pandas: ≥ 2.0.0 (было 1.0.5)

    • NumPy: ≥ 1.21 (было 1.15)

    • PyArrow: ≥ 11.0.0 (было 4.0.0)

2. Рефакторинг для питонщиков (PySpark API):

Pandas API on Spark (бывший Koalas) претерпел серьёзную чистку, чтобы лучше соответствовать Pandas 2.x. Готовьтесь править код, так как удалены:

  • Методы .iteritems() для DataFrame/Series (используйте .items()).

  • Методы .append() для DataFrame/Series (используйте ps.concat()).

  • Методы .mad() (Median Absolute Deviation) для DataFrame/Series.

  • Индексы Int64Index, Float64Index (используйте базовый Index).

  • Параметр inplace=True во многих методах (операции теперь всегда возвращают новый объект).

  • Параметры include_start/include_end в .between_time() (используйте inclusive).

  • Параметр na_sentinel в .factorize() (используйте use_na_sentinel).

  • Параметр squeeze в ps.read_csv()/ps.read_excel().

  • Изменилось поведение некоторых функций (Series.str.replace, value_counts, DataFrame.stack и др.) для большего соответствия Pandas.

3. Изменения в поведении SQL:

  • ANSI SQL режим включён по умолчанию (spark.sql.ansi.enabled=true). Самое важное изменение! Запросы станут строже к типам данных, переполнениям и другим нюансам стандарта SQL. Это может сломать старые запросы, которые полагались на не-ANSI поведение (например, возврат null вместо ошибки при некорректном касте). Можно временно отключить флагом, но рекомендуется адаптировать код.

  • Изменения дефолтных значений конфигов

    • spark.sql.sources.default. Теперь используется по умолчанию при CREATE TABLE без USING (раньше был Hive);

    • spark.sql.maxSinglePartitionBytes. Ограничен 128 MB (раньше был безлимитным);

    • spark.sql.orc.compression.codec: Теперь zstd (раньше был snappy).

  • Удалены старые (.legacy) флаги. Например, для rebase дат/времён в Parquet/Avro.

  • Более строгое поведение. Например, при касте timestamp в числа с переполнением (non-ANSI), при обработке ошибок кодировки в encode/decode, при чтении повреждённых файлов (даже с ignoreCorruptFiles=true в некоторых случаях).

  • Нормализация ключей в map. -0.0 теперь автоматически становится 0.0 при создании map.

  • Запрет недокументированного синтаксиса. Например, использование ! вместо NOT вне Boolean-выражений или указание типов/constraints в CREATE VIEW.

4. Другие удаления:

  • Удалено имя кодека lz4raw для Parquet (используйте lz4_raw).

  • Удалена зависимость hive-llap-common.

Что делать?

Главный совет: внимательно изучить официальные гайды по миграции для Spark SQL, PySpark и других компонентов, которые вы используете. Они содержат полный список изменений и рекомендации по адаптации кода. Перед обновлением на продакшене обязательно тщательно протестируйте ваши приложения на Spark 4.0 в тестовом окружении.

Заключение и вердикт

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

Какие трудности ждут при переходе?

  • Breaking Changes. Их немало, особенно в PySpark API и поведении SQL (ANSI). Без рефакторинга кода не обойтись.

  • Обновление зависимостей. Необходимость перехода на новые версии Scala, Java, Python и ключевых библиотек потребует усилий по обновлению окружений и пересборке проектов.

  • Совместимость. Нужно проверять совместимость со всеми сторонними коннекторами и библиотеками.

  • Тестирование. Миграция потребует значительных ресурсов на тщательное тестирование логики и производительности.

Итог

Spark 4.0 — это мощный шаг вперёд, делающий фреймворк более современным, гибким и удобным для решения широкого круга задач, особенно для python-экосистемы и работы с полуструктурированными данными. Однако переход требует осознанных усилий. Не спешите сломя голову обновлять production, но и не игнорируйте новый релиз. Изучите его возможности, оцените потенциальные выгоды и риски для ваших проектов и спланируйте миграцию, когда будете к ней готовы.

Комментарии (0)