Условие
Аналитик жалуется, что pandas-скрипт обрабатывает 50M строк по 4 часа и иногда падает с MemoryError. Стоит ли переходить на Spark? Когда переход оправдан, а когда — нет?
Решение
Подход
Сначала — оптимизировать pandas.
Перед прыжком на Spark проверьте, что pandas использован эффективно:
- Типы данных.
int64 → int32,float64 → float32,object → category. Часто экономит 50-80% памяти. - Чтение по чанкам:
pd.read_csv(..., chunksize=1_000_000)+ агрегация по чанкам. - Векторизация. Избавиться от
.applyиfor, использоватьgroupby,merge,transform. - Полярный путь: попробовать
polars— часто 10× быстрее pandas на одной машине, поддерживает lazy. - DuckDB: in-process OLAP с SQL. Прекрасно делает joins по multi-GB CSV/Parquet на ноутбуке.
После этих оптимизаций 50M строк часто становятся 30 минут на сильном ноутбуке — Spark не нужен.
Когда Spark действительно нужен.
- Данные не помещаются на одну машину (сотни ГБ, ТБ).
- Регулярные пайплайны на схожих объёмах — DAG, оркестрация, fault tolerance.
- Распределённые joins больших таблиц.
- ML на больших данных (Spark MLlib).
- Streaming (Spark Structured Streaming).
Когда Spark — overkill.
- Меньше 50-100 ГБ умещается в памяти ноды → DuckDB / Polars / Dask на одной мощной машине дешевле.
- Один-разовая аналитика, а не повторяемый pipeline.
- Маленькая команда без DevOps — поддержка Spark-кластера дорого.
Сравнение производительности на 50M строк.
| Tool | Время | Память | Сложность |
|---|---|---|---|
| pandas (наивно) | 4 ч + crash | 60 ГБ | средняя |
| pandas (оптимизировано) | 30 мин | 8 ГБ | средняя |
| polars | 5-10 мин | 4 ГБ | низкая |
| DuckDB (SQL) | 5 мин | 4 ГБ | низкая |
| Spark (local) | 15 мин | 8 ГБ | высокая |
| Spark (кластер 8 нод) | 1-2 мин | распределённо | очень высокая |
# Если всё-таки Spark
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("analytics").getOrCreate()
df = spark.read.parquet("s3://bucket/data/")
agg = (df.groupBy("user_id")
.agg({"amount": "sum"})
.orderBy("sum(amount)", ascending=False))
agg.write.mode("overwrite").parquet("s3://bucket/agg/")Подводные камни
- Spark «бесплатно ускоряет» — миф. Spark медленнее pandas на маленьких данных из-за overhead JVM, сериализации, shuffle.
- Driver memory. Часто рушится не executor, а driver, если делать
.collect()на большом dataframe. Никогда не.collect()без проверки размера. - Skewed joins. Один ключ с 90% данных = одна executor-таска делает всю работу. Решается salt-keys.
- Lazy evaluation Spark. Цепочка трансформаций ничего не делает;
.show()/.write()запускает план. Без.cache()повторные.count()пересчитывают всё. - Parquet вместо CSV. Хранение в parquet с partition + pushdown predicates даёт ×10 ускорение, независимо от движка.
Эталонный ответ
Сначала оптимизируйте pandas (типы, chunks, polars/DuckDB) — часто этого хватает. Spark оправдан, когда данные не помещаются на одну машину, нужен распределённый join, или есть продакшен-pipeline на десятках ГБ ежедневно. Для разовой аналитики на 50M строк DuckDB или polars обычно быстрее, проще и дешевле.