Привет, Хабр! Я Станислав Габдулгазиев, архитектор департамента поддержки продаж 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, но и не игнорируйте новый релиз. Изучите его возможности, оцените потенциальные выгоды и риски для ваших проектов и спланируйте миграцию, когда будете к ней готовы.