Spark memory tuning на собеседовании Data Engineer

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Карьерник — 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 384MB

YARN allocates executor.memory + memoryOverhead per executor.

Spark Memory:

spark.memory.fraction = 0.6        # 60% памяти после reserved
spark.memory.storageFraction = 0.5  # внутри Spark Memory — 50% storage, остальное execution

Storage и 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».

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

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.

Связанные темы

FAQ

Сколько executors на cluster?

Зависит от cores. Эмпирика: 5 cores per executor, потом N executors = total cores / 5.

Это официальная информация?

Нет. Статья основана на документации Spark 3.x.


Тренируйте Data Engineering — откройте тренажёр с 1500+ вопросами для собесов.