Привет, Хабр! Я Станислав Габдулгазиев, архитектор департамента поддержки продаж 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).Изменилось поведение некоторых функций (Series.str.replace, value_counts, DataFrame.stack и др.) для большего соответствия Pandas.
3. Изменения в поведении SQL:
ANSI SQL режим включён по умолчанию (spark.sql.ansi.enabled=true). Самое важное изменение! Запросы станут строже к типам данных, переполнениям и другим нюансам стандарта SQL. Это может сломать старые запросы, которые полагались на не-ANSI поведение (например, возврат null вместо ошибки при некорректном касте). Можно временно отключить флагом, но рекомендуется адаптировать код.
Изменения дефолтных значений конфигов
spark.sql.sources.defaul
t. Теперь используется по умолчанию при 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, но и не игнорируйте новый релиз. Изучите его возможности, оцените потенциальные выгоды и риски для ваших проектов и спланируйте миграцию, когда будете к ней готовы.