Spark memory tuning на собеседовании Data Engineer
Карьерник — Duolingo для аналитиков: 10 минут в день тренируй SQL, Python, A/B, статистику, метрики и ещё 3 темы собеса. 1500+ вопросов в Telegram-боте. Бесплатно.
Содержание:
Зачем спрашивают на собесе DE
Spark OOM — типичная production боль. На собесе DE: «как тюнить executor memory», «что такое spark.memory.fraction», «причины OOM».
Структура памяти executor
┌─ Executor JVM ─────────────────────────┐
│ │
│ ┌─ Reserved (300MB) ──────┐ │
│ ├─ User Memory ───────────┤ │
│ │ │ │
│ ├─ Spark Memory ──────────┤ │
│ │ ├─ Storage (cache) │ │
│ │ └─ Execution (shuffle) │ │
│ │ │ │
│ └─ Off-heap ──────────────┘ │
│ │
└──────────────────────────────────────────┘spark.executor.memory — JVM heap.
spark.executor.memoryOverhead — non-JVM (off-heap, native libs).
Total = executor.memory + memoryOverhead.
executor.memory и overhead
spark.executor.memory = 8g
spark.executor.memoryOverhead = 2g # default = 10% of executor.memory or 384MBYARN allocates executor.memory + memoryOverhead per executor.
Spark Memory:
spark.memory.fraction = 0.6 # 60% памяти после reserved
spark.memory.storageFraction = 0.5 # внутри Spark Memory — 50% storage, остальное executionStorage и Execution могут друг друга вытеснять (unified memory model).
OOM — диагностика
Java heap space. Чаще всего — слишком много данных на executor:
- Большой shuffle (skew, broadcast не сработал).
- Огромный collect / take.
- Каскадные UDF.
Лечение:
- Поднять executor.memory.
- Увеличить partitions (меньше per-partition).
- Разнести broadcast.
- Заменить UDF на встроенные.
OOM in Container. Вышли за overhead:
- Off-heap usage большой (Tungsten, native libs, Python in PySpark).
- Поднять
memoryOverhead.
Spill to disk. Не OOM, но медленно. Видно в Spark UI «Shuffle Spill».
Partitioning для памяти
Партиции должны быть «правильного размера»:
- Слишком маленькие (1MB) → overhead на task.
- Слишком большие (>1GB) → OOM в одной task.
Эмпирика: partition 100-200MB.
# увеличить число партиций
df = df.repartition(2000)
df = df.coalesce(500) # без shuffle, только уменьшениеspark.sql.shuffle.partitions = 200 (default). На больших данных — 1000-4000.
Skew. Один partition в 10× больше других. Lечение:
- Salt (добавить random suffix к ключу).
- Skew join hint (
/*+ SKEW(...) */). - AQE skew handling.
Off-heap и Tungsten
Tungsten хранит данные off-heap (вне JVM heap). Меньше GC, эффективнее.
spark.memory.offHeap.enabled = true
spark.memory.offHeap.size = 4gДоступно для executor.memoryOverhead.
PySpark. Python работает off-heap. Нужно memoryOverhead enough.
Частые ошибки
Поднимать executor.memory вместо partitions. Память не безграничная. Иногда лучше больше партиций.
Игнорировать GC. Long GC pauses → tasks умирают по timeout. Setting -XX:+UseG1GC для больших heaps.
Slot size mismatch. На YARN — executor.memory должен быть совместим с node manager max alloc.
Cache огромных DataFrames. df.cache() без необходимости — забивает Storage memory, шевелит Execution memory.
collect() на больших. Driver OOM. Используй take(N), write, или toLocalIterator.
Не использовать AQE. AQE handles skew, coalesces shuffle automatically.
Связанные темы
- Spark RDD vs DataFrame для DE
- Spark Catalyst и AQE для DE
- Spark broadcast joins для DE
- Spark shuffle и skew на собесе DE
- Подготовка к собесу Data Engineer
FAQ
Сколько executors на cluster?
Зависит от cores. Эмпирика: 5 cores per executor, потом N executors = total cores / 5.
Это официальная информация?
Нет. Статья основана на документации Spark 3.x.
Тренируйте Data Engineering — откройте тренажёр с 1500+ вопросами для собесов.