Apache Spark Tuning: Entendendo e Otimizando o Catalyst
A Anatomia de um Job Spark
Para otimizar o Spark, você precisa entender como ele distribui o trabalho. O Driver coordena, os Executores executam.
Adaptive Query Execution (AQE)
Introduzido no Spark 3.0, o AQE é a ferramenta mais poderosa de otimização automática. Ele ajusta o plano de execução durante a execução, baseando-se em estatísticas reais dos dados.
# Configurações essenciais para Spark 3.x
spark.conf.set("spark.sql.adaptive.enabled", "true")
spark.conf.set("spark.sql.adaptive.coalescePartitions.enabled", "true")
spark.conf.set("spark.sql.adaptive.skewJoin.enabled", "true")
Resolvendo Data Skew (Assimetria)
Skew acontece quando uma chave de partição (ex: country_code='US') tem muito mais dados que as outras. Um executor fica trabalhando horas enquanto os outros terminam em minutos.
Solução 1: AQE Skew Join
Com a config acima, o Spark detecta partições grandes e as divide automaticamente em sub-partições menores para paralelizar o trabalho.
Solução 2: Salting (Manual)
Se o AQE não resolver, use "Salting". Adicione uma coluna com números aleatórios à chave de join para forçar uma redistribuição uniforme.
from pyspark.sql.functions import rand, floor
# Adiciona "salt" de 0 a 19
df_skewed = df.withColumn("salt", floor(rand() * 20))
df_uniform = df_small.withColumn("salt", explode(array([lit(i) for i in range(20)])))
# Join usando a chave original + salt
df_joined = df_skewed.join(df_uniform, ["join_key", "salt"])
Memory Tuning: OOM (Out Of Memory)
Erros de OOM geralmente ocorrem por falta de memória no Heap da JVM. Entenda as duas regiões principais:
- Execution Memory: Usada para Shuffles, Joins, Sorts.
- Storage Memory: Usada para Cache (persist).
Se você não usa muito .cache() ou .persist(), aumente a memória de execução diminuindo a fração de storage:
--conf spark.memory.fraction=0.8
--conf spark.memory.storageFraction=0.3
Conclusão
Otimização de Spark é iterativa. Use a aba "SQL" do Spark UI para identificar o estágio mais lento (o que tem as barras mais longas) e ataque o gargalo específico (I/O, CPU ou Network).
./SUBSCRIBE_UPDATES
// Receba dicas de FinOps, cases de otimização e novidades sobre engenharia de dados.